Skip to content

Commit 0123e5e

Browse files
committed
drivers: smbus: provide packet-error-checking (pec) routines
In the case that SMBus hardware does not automatically perform packet error checking (PEC), provide generic inline functions in `zephyr/drivers/smbus.h` that can be used by drivers to perform PEC in software. Signed-off-by: Chris Friedt <[email protected]> Signed-off-by: Andrew Lewycky <[email protected]>
1 parent d90d71c commit 0123e5e

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

drivers/smbus/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
menuconfig SMBUS
77
bool "System Management Bus (SMBus) drivers"
8+
imply CRC
89
help
910
Enable SMBus Driver Configuration
1011

drivers/smbus/smbus_utils.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include "smbus_utils.h"
88

99
#include <zephyr/logging/log.h>
10+
#include <zephyr/sys/__assert.h>
11+
#include <zephyr/sys/crc.h>
12+
#include <zephyr/sys/util.h>
1013

1114
LOG_MODULE_REGISTER(smbus_utils, CONFIG_SMBUS_LOG_LEVEL);
1215

@@ -41,3 +44,71 @@ void smbus_loop_alert_devices(const struct device *dev, sys_slist_t *callbacks)
4144
smbus_fire_callbacks(callbacks, dev, address);
4245
}
4346
}
47+
48+
uint8_t smbus_pec_num_msgs(uint32_t flags, uint8_t num_msgs)
49+
{
50+
__ASSERT_NO_MSG(num_msgs != 0);
51+
52+
if ((flags & SMBUS_MODE_PEC) == 0) {
53+
return num_msgs - 1;
54+
}
55+
56+
return num_msgs;
57+
}
58+
59+
uint8_t smbus_pec(uint16_t addr, const struct i2c_msg *msgs, uint8_t num_msgs)
60+
{
61+
uint8_t pec = 0;
62+
uint8_t prior_direction = 0;
63+
uint8_t addr8 = addr & BIT_MASK(7);
64+
65+
for (uint8_t i = 0; i < num_msgs; i++) {
66+
/* When direction changes, there is a repeated start byte. */
67+
uint8_t start_byte;
68+
uint8_t direction = msgs[i].flags & I2C_MSG_RW_MASK;
69+
70+
if ((i == 0) || (direction != prior_direction)) {
71+
prior_direction = direction;
72+
start_byte = (addr8 << 1) | direction;
73+
pec = crc8_ccitt(pec, &start_byte, sizeof(start_byte));
74+
}
75+
76+
pec = crc8_ccitt(pec, msgs[i].buf, msgs[i].len);
77+
}
78+
79+
return pec;
80+
}
81+
82+
void smbus_write_prepare_pec(uint32_t flags, uint16_t addr, struct i2c_msg *msgs, uint8_t num_msgs)
83+
{
84+
if ((flags & SMBUS_MODE_PEC) == 0) {
85+
return;
86+
}
87+
88+
__ASSERT_NO_MSG(msgs != NULL);
89+
__ASSERT_NO_MSG(num_msgs != 0);
90+
__ASSERT_NO_MSG(msgs[num_msgs - 1].buf != NULL);
91+
92+
msgs[num_msgs - 1].buf[0] = smbus_pec(addr, msgs, num_msgs - 1);
93+
}
94+
95+
int smbus_read_check_pec(uint32_t flags, uint16_t addr, const struct i2c_msg *msgs,
96+
uint8_t num_msgs)
97+
{
98+
if ((flags & SMBUS_MODE_PEC) == 0) {
99+
return 0;
100+
}
101+
102+
__ASSERT_NO_MSG(num_msgs != 0);
103+
__ASSERT_NO_MSG(msgs != NULL);
104+
__ASSERT_NO_MSG(msgs[num_msgs - 1].buf != NULL);
105+
106+
uint8_t reported_pec = msgs[num_msgs - 1].buf[0];
107+
uint8_t computed_pec = smbus_pec(addr, msgs, num_msgs - 1);
108+
109+
if (reported_pec != computed_pec) {
110+
return -EIO;
111+
}
112+
113+
return 0;
114+
}

drivers/smbus/smbus_utils.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
#ifndef ZEPHYR_DRIVERS_SMBUS_SMBUS_UTILS_H_
88
#define ZEPHYR_DRIVERS_SMBUS_SMBUS_UTILS_H_
99

10+
#include <errno.h>
1011
#include <stdint.h>
12+
1113
#include <zephyr/device.h>
14+
#include <zephyr/drivers/i2c.h>
1215
#include <zephyr/drivers/smbus.h>
1316
#include <zephyr/sys/slist.h>
1417

@@ -107,4 +110,82 @@ static inline void smbus_init_callback(struct smbus_callback *callback,
107110
*/
108111
void smbus_loop_alert_devices(const struct device *dev, sys_slist_t *callbacks);
109112

113+
/**
114+
* @brief Compute the number of messages required for an SMBus transaction
115+
*
116+
* If @p flags indicates that the transaction requires packet error checking (PEC),
117+
* the number of messages is equal to @p num_msgs, otherwise the number of messages
118+
* required is @p num_msgs - 1, since a PEC byte is not required.
119+
*
120+
* Callers are expected to allocated an array of @ref i2c_msg objects to hold a number of
121+
* i2c messages, including one message dedicated to the a PEC byte, whether or not PEC is
122+
* being used.
123+
*
124+
* @note This function is intended for SMBus drivers that do not have hardware PEC support and
125+
* therefore must rely on software PEC calculation.
126+
*
127+
* @param flags SMBus flags.
128+
* @param num_msgs Number of allocated messages.
129+
* @return The number of required messages.
130+
*/
131+
uint8_t smbus_pec_num_msgs(uint32_t flags, uint8_t num_msgs);
132+
133+
/**
134+
* @brief Compute the packet error checking (PEC) byte for an SMBus transaction
135+
*
136+
* @note This function is intended for SMBus drivers that do not have hardware PEC support and
137+
* therefore must rely on software PEC calculation.
138+
*
139+
* @note At this time, only 7-bit addresses are supported in @p addr.
140+
*
141+
* @param addr The address of the target device.
142+
* @param msgs Array of @ref i2c_msg that make up the transaction.
143+
* @param num_msgs Number of messages in the transaction.
144+
* @return the computed PEC byte.
145+
*/
146+
uint8_t smbus_pec(uint16_t addr, const struct i2c_msg *msgs, uint8_t num_msgs);
147+
148+
/**
149+
* @brief Prepare the packet error checking (PEC) byte for an SMBus write transaction
150+
*
151+
* If the @p flags bitmask contains @ref SMBUS_MODE_PEC (i.e. PEC is enabled), the number of
152+
* messages is equal to @p num_msgs, otherwise the number of messages required is @p num_msgs - 1,
153+
* since a PEC byte is not required.
154+
*
155+
* @note This function is intended for SMBus drivers that do not have hardware PEC support and
156+
* therefore must rely on software PEC calculation.
157+
*
158+
* @note At this time, only 7-bit addresses are supported in @p addr.
159+
*
160+
* @param flags SMBus flags.
161+
* @param addr The address of the target device.
162+
* @param messages Array of @ref i2c_msg objects that make up the transaction.
163+
* @param num_msgs Number of messages in the transaction.
164+
*/
165+
void smbus_write_prepare_pec(uint32_t flags, uint16_t addr, struct i2c_msg *msgs, uint8_t num_msgs);
166+
167+
/**
168+
* @brief Check the packet error checking (PEC) byte for an SMBus read transaction
169+
*
170+
* If the @p flags bitmask contains @ref SMBUS_MODE_PEC (i.e. PEC is enabled), the number of
171+
* messages is equal to @p num_msgs, otherwise the number of messages required is
172+
* @p num_msgs - 1, since a PEC byte is not required.
173+
*
174+
* When PEC is not enabled, this function returns 0.
175+
*
176+
* @note This function is intended for SMBus drivers that do not have hardware PEC support and
177+
* therefore must rely on software PEC calculation.
178+
*
179+
* @note At this time, only 7-bit addresses are supported in @p addr.
180+
*
181+
* @param flags SMBus flags.
182+
* @param addr The address of the target device.
183+
* @param messages Array of @ref i2c_msg objects that make up the transaction.
184+
* @param num_msgs Number of messages in the transaction.
185+
* @retval 0 on success.
186+
* @retval -EIO if the PEC byte does not match the computed value.
187+
*/
188+
int smbus_read_check_pec(uint32_t flags, uint16_t addr, const struct i2c_msg *msgs,
189+
uint8_t num_msgs);
190+
110191
#endif /* ZEPHYR_DRIVERS_SMBUS_SMBUS_UTILS_H_ */

0 commit comments

Comments
 (0)