Skip to content

Commit f82d96e

Browse files
committed
drivers: i2c: i2c_mspm0g3xxx: add watchdog timer
When the system is crashing, the I2C lines (SDA/SCL) are not being reset and might stay LOW. To address this issue, we need to ensure that none of the user i2c driver callbacks (that are doing reads or writes) are taking longer than CONFIG_I2C_MSPM0G3XXX_WATCHDOG_TIMEOUT. So we fire a timer before each call. If the callback doesn't finish fast enough, the watchdog callback will reset the i2c lines, NACK the i2c message and panic with the CONFIG_MSPM0G3XXX_I2C_WATCHDOG_PANIC_CODE error code. The timeout is configured with CONFIG_I2C_MSPM0G3XXX_WATCHDOG_TIMEOUT The panic error code is configured with CONFIG_MSPM0G3XXX_I2C_WATCHDOG_PANIC_CODE. To make sure of this reboot feature, the device tree needs to provide a reference to a hardware timer. Let's say we want to use timer timg7 as a watchdog: We need to configure that timer in the dts: ``` &timg7 { status = "okay"; mode = <ONE_SHOT_DOWN>; prescaler = <0>; divide-ratio = <RATE_1>; }; ``` and then reference in the i2c block: ``` &i2c0 { watchdog-timer = <&timg7>; extras ... ``` Since we are invoking a software panic on the mcu, make sure to disable the COREDUMP logging as it will introduce performance penalties and delay the mcu reboot. This can be done with: ``` CONFIG_DEBUG_COREDUMP=n ``` Signed-off-by: Dimitris Karnikis <[email protected]>
1 parent 5f96bf1 commit f82d96e

File tree

1 file changed

+78
-1
lines changed

1 file changed

+78
-1
lines changed

drivers/i2c/i2c_mspm0g3xxx.c

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <zephyr/kernel.h>
77
#include <zephyr/drivers/i2c.h>
88
#include <zephyr/drivers/pinctrl.h>
9+
#include <zephyr/drivers/counter.h>
910
#include <soc.h>
1011

1112
/* Logging includes */
@@ -76,6 +77,7 @@ struct i2c_mspm0g3xxx_config {
7677
const struct pinctrl_dev_config *pinctrl;
7778
void (*interrupt_init_function)(const struct device *dev);
7879
uint32_t dt_bitrate;
80+
const struct device *watchdog_timer;
7981
};
8082

8183
struct i2c_mspm0g3xxx_data {
@@ -92,6 +94,7 @@ struct i2c_mspm0g3xxx_data {
9294
int target_rx_valid;
9395
struct k_sem i2c_busy_sem;
9496
struct k_sem transfer_timeout_sem;
97+
struct counter_alarm_cfg watchdog_timer_cfg;
9598
};
9699

97100
#ifdef CONFIG_I2C_MSPM0G3XXX_TARGET_SUPPORT
@@ -111,6 +114,57 @@ static K_KERNEL_STACK_DEFINE(i2c_mspm0g3xxx_target_stack,
111114
CONFIG_I2C_MSPM0G3XXX_TARGET_THREAD_STACK_SIZE);
112115
static struct k_thread i2c_mspm0g3xxx_target_thread;
113116

117+
static void i2c_mspm0g3xxx_target_stop_watchdog(struct i2c_mspm0g3xxx_data *data) {
118+
const struct i2c_mspm0g3xxx_config *config = data->cfg;
119+
120+
if (!config->watchdog_timer)
121+
return;
122+
123+
int err = counter_stop(config-> watchdog_timer);
124+
if (err != 0) {
125+
LOG_ERR("Failed to stop the timer, err: %d", err);
126+
}
127+
128+
err = counter_cancel_channel_alarm(config->watchdog_timer, 0);
129+
if (err != 0) {
130+
LOG_ERR("Failed to cancel the timer alarm, err: %d", err);
131+
}
132+
}
133+
134+
static void i2c_mspm0g3xxx_target_reset(const struct device *dev, uint8_t channel, uint32_t ticks,
135+
void *user_data)
136+
{
137+
struct i2c_mspm0g3xxx_data *data = user_data;
138+
const struct i2c_mspm0g3xxx_config *config = data->cfg;
139+
140+
DL_I2C_setTargetACKOverrideValue((I2C_Regs *)config->base,
141+
DL_I2C_TARGET_RESPONSE_OVERRIDE_VALUE_NACK);
142+
DL_I2C_disableTargetClockStretching((I2C_Regs *)config->base);
143+
DL_I2C_disablePower((I2C_Regs *)config->base);
144+
z_except_reason(CONFIG_I2C_MSPM0G3XXX_WATCHDOG_PANIC_CODE);
145+
}
146+
147+
static void i2c_mspm0g3xxx_target_start_watchdog(struct i2c_mspm0g3xxx_data *data)
148+
{
149+
const struct i2c_mspm0g3xxx_config *config = data->cfg;
150+
struct counter_alarm_cfg *watchdog_timer_cfg = &data->watchdog_timer_cfg;
151+
152+
if (!config->watchdog_timer)
153+
return;
154+
155+
i2c_mspm0g3xxx_target_stop_watchdog(data);
156+
157+
int err = counter_set_channel_alarm(config->watchdog_timer, 0, watchdog_timer_cfg);
158+
if (err != 0) {
159+
LOG_ERR("Failed to cancel the timer alarm, err: %d", err);
160+
}
161+
162+
err = counter_start(config->watchdog_timer);
163+
if (err != 0) {
164+
LOG_ERR("Failed to start the timer, err: %d", err);
165+
}
166+
}
167+
114168
void i2c_mspm0g3xxx_target_thread_work(void)
115169
{
116170
struct i2c_mspm0g3xxx_target_msg target_msg;
@@ -142,8 +196,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
142196
if (data->state == I2C_mspm0g3xxx_TARGET_STARTED) {
143197
data->state = I2C_mspm0g3xxx_TARGET_RX_INPROGRESS;
144198
if (tconfig->callbacks->write_requested != NULL) {
199+
i2c_mspm0g3xxx_target_start_watchdog(data);
145200
data->target_rx_valid =
146201
tconfig->callbacks->write_requested(tconfig);
202+
i2c_mspm0g3xxx_target_stop_watchdog(data);
147203
}
148204
}
149205
/* Store received data in buffer */
@@ -154,9 +210,11 @@ void i2c_mspm0g3xxx_target_thread_work(void)
154210
if (data->target_rx_valid == 0) {
155211
nextByte = DL_I2C_receiveTargetData(
156212
(I2C_Regs *)config->base);
213+
i2c_mspm0g3xxx_target_start_watchdog(data);
157214
data->target_rx_valid =
158215
tconfig->callbacks->write_received(
159216
tconfig, nextByte);
217+
i2c_mspm0g3xxx_target_stop_watchdog(data);
160218

161219
if (data->target_rx_valid == 0) {
162220
DL_I2C_setTargetACKOverrideValue(
@@ -183,8 +241,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
183241
/* Fill TX FIFO if there are more bytes to send */
184242
if (tconfig->callbacks->read_requested != NULL) {
185243
uint8_t nextByte;
244+
i2c_mspm0g3xxx_target_start_watchdog(data);
186245
data->target_tx_valid =
187246
tconfig->callbacks->read_requested(tconfig, &nextByte);
247+
i2c_mspm0g3xxx_target_stop_watchdog(data);
188248
if (data->target_tx_valid == 0) {
189249
DL_I2C_transmitTargetData((I2C_Regs *)config->base,
190250
nextByte);
@@ -203,8 +263,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
203263
* will be filled in */
204264
uint8_t nextByte;
205265
if (data->target_tx_valid == 0) {
266+
i2c_mspm0g3xxx_target_start_watchdog(data);
206267
data->target_tx_valid = tconfig->callbacks->read_processed(
207268
tconfig, &nextByte);
269+
i2c_mspm0g3xxx_target_stop_watchdog(data);
208270
}
209271

210272
if (data->target_tx_valid == 0) {
@@ -221,7 +283,9 @@ void i2c_mspm0g3xxx_target_thread_work(void)
221283
data->state = I2C_mspm0g3xxx_IDLE;
222284
k_sem_give(&data->i2c_busy_sem);
223285
if (tconfig->callbacks->stop) {
286+
i2c_mspm0g3xxx_target_start_watchdog(data);
224287
tconfig->callbacks->stop(tconfig);
288+
i2c_mspm0g3xxx_target_stop_watchdog(data);
225289
}
226290
break;
227291
default:
@@ -768,12 +832,24 @@ static int i2c_mspm0g3xxx_init(const struct device *dev)
768832
config->clock_frequency);
769833
return -EINVAL;
770834
}
835+
771836
LOG_DBG("DT bitrate %uHz (%u)", config->clock_frequency,
772837
config->dt_bitrate);
773838

774839
k_sem_init(&data->i2c_busy_sem, 0, 1);
775840
k_sem_init(&data->transfer_timeout_sem, 1, 1);
776841

842+
if (config->watchdog_timer) {
843+
if (!device_is_ready(config->watchdog_timer)) {
844+
LOG_ERR("Watchdog timer is not ready");
845+
return -EINVAL;
846+
}
847+
848+
data->watchdog_timer_cfg.user_data = (void *)data;
849+
data->watchdog_timer_cfg.callback = i2c_mspm0g3xxx_target_reset;
850+
data->watchdog_timer_cfg.ticks = counter_us_to_ticks(config->watchdog_timer, CONFIG_I2C_MSPM0G3XXX_WATCHDOG_TIMEOUT);
851+
}
852+
777853
/* Init power */
778854
DL_I2C_reset((I2C_Regs *)config->base);
779855
DL_I2C_enablePower((I2C_Regs *)config->base);
@@ -895,6 +971,7 @@ static const struct i2c_driver_api i2c_mspm0g3xxx_driver_api = {
895971
.divideRatio = DL_I2C_CLOCK_DIVIDE_1}, \
896972
.dt_bitrate = CALC_DT_BITRATE(DT_INST_PROP(index, clock_frequency), \
897973
DT_INST_PROP_BY_PHANDLE(index, clocks, clock_frequency)), \
974+
.watchdog_timer = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(DT_DRV_INST(index), watchdog_timer)),\
898975
}; \
899976
\
900977
static struct i2c_mspm0g3xxx_data i2c_mspm0g3xxx_data_##index = { \
@@ -903,7 +980,7 @@ static const struct i2c_driver_api i2c_mspm0g3xxx_driver_api = {
903980
\
904981
I2C_DEVICE_DT_INST_DEFINE(index, i2c_mspm0g3xxx_init, NULL, &i2c_mspm0g3xxx_data_##index, \
905982
&i2c_mspm0g3xxx_cfg_##index, POST_KERNEL, \
906-
CONFIG_I2C_INIT_PRIORITY, &i2c_mspm0g3xxx_driver_api); \
983+
CONFIG_COUNTER_INIT_PRIORITY, &i2c_mspm0g3xxx_driver_api); \
907984
\
908985
INTERRUPT_INIT_FUNCTION(index)
909986

0 commit comments

Comments
 (0)