Skip to content

Commit b6bb7c2

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 use 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 since the default backend is the "logging" one and it takes too much of the cpu resources, thus delaying the mcu reboot. This can be done with: ``` CONFIG_DEBUG_COREDUMP=n ``` Of course for now, the implementation is using a counter timer since we don't have a mspm0 watchdog. We need to also use I2C_MSPM0G3XXX_INIT_PRIORITY as the driver init priority. Signed-off-by: Dimitris Karnikis <dika@bang-olufsen.dk>
1 parent 90f9f4c commit b6bb7c2

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

drivers/i2c/i2c_mspm0g3xxx.c

Lines changed: 80 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,58 @@ 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+
if (!config->watchdog_timer) {
120+
return;
121+
}
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+
156+
i2c_mspm0g3xxx_target_stop_watchdog(data);
157+
158+
int err = counter_set_channel_alarm(config->watchdog_timer, 0, watchdog_timer_cfg);
159+
if (err != 0) {
160+
LOG_ERR("Failed to cancel the timer alarm, err: %d", err);
161+
}
162+
163+
err = counter_start(config->watchdog_timer);
164+
if (err != 0) {
165+
LOG_ERR("Failed to start the timer, err: %d", err);
166+
}
167+
}
168+
114169
void i2c_mspm0g3xxx_target_thread_work(void)
115170
{
116171
struct i2c_mspm0g3xxx_target_msg target_msg;
@@ -142,8 +197,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
142197
if (data->state == I2C_mspm0g3xxx_TARGET_STARTED) {
143198
data->state = I2C_mspm0g3xxx_TARGET_RX_INPROGRESS;
144199
if (tconfig->callbacks->write_requested != NULL) {
200+
i2c_mspm0g3xxx_target_start_watchdog(data);
145201
data->target_rx_valid =
146202
tconfig->callbacks->write_requested(tconfig);
203+
i2c_mspm0g3xxx_target_stop_watchdog(data);
147204
}
148205
}
149206
/* Store received data in buffer */
@@ -154,9 +211,11 @@ void i2c_mspm0g3xxx_target_thread_work(void)
154211
if (data->target_rx_valid == 0) {
155212
nextByte = DL_I2C_receiveTargetData(
156213
(I2C_Regs *)config->base);
214+
i2c_mspm0g3xxx_target_start_watchdog(data);
157215
data->target_rx_valid =
158216
tconfig->callbacks->write_received(
159217
tconfig, nextByte);
218+
i2c_mspm0g3xxx_target_stop_watchdog(data);
160219

161220
if (data->target_rx_valid == 0) {
162221
DL_I2C_setTargetACKOverrideValue(
@@ -183,8 +242,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
183242
/* Fill TX FIFO if there are more bytes to send */
184243
if (tconfig->callbacks->read_requested != NULL) {
185244
uint8_t nextByte;
245+
i2c_mspm0g3xxx_target_start_watchdog(data);
186246
data->target_tx_valid =
187247
tconfig->callbacks->read_requested(tconfig, &nextByte);
248+
i2c_mspm0g3xxx_target_stop_watchdog(data);
188249
if (data->target_tx_valid == 0) {
189250
DL_I2C_transmitTargetData((I2C_Regs *)config->base,
190251
nextByte);
@@ -203,8 +264,10 @@ void i2c_mspm0g3xxx_target_thread_work(void)
203264
* will be filled in */
204265
uint8_t nextByte;
205266
if (data->target_tx_valid == 0) {
267+
i2c_mspm0g3xxx_target_start_watchdog(data);
206268
data->target_tx_valid = tconfig->callbacks->read_processed(
207269
tconfig, &nextByte);
270+
i2c_mspm0g3xxx_target_stop_watchdog(data);
208271
}
209272

210273
if (data->target_tx_valid == 0) {
@@ -221,7 +284,9 @@ void i2c_mspm0g3xxx_target_thread_work(void)
221284
data->state = I2C_mspm0g3xxx_IDLE;
222285
k_sem_give(&data->i2c_busy_sem);
223286
if (tconfig->callbacks->stop) {
287+
i2c_mspm0g3xxx_target_start_watchdog(data);
224288
tconfig->callbacks->stop(tconfig);
289+
i2c_mspm0g3xxx_target_stop_watchdog(data);
225290
}
226291
break;
227292
default:
@@ -768,12 +833,25 @@ static int i2c_mspm0g3xxx_init(const struct device *dev)
768833
config->clock_frequency);
769834
return -EINVAL;
770835
}
836+
771837
LOG_DBG("DT bitrate %uHz (%u)", config->clock_frequency,
772838
config->dt_bitrate);
773839

774840
k_sem_init(&data->i2c_busy_sem, 0, 1);
775841
k_sem_init(&data->transfer_timeout_sem, 1, 1);
776842

843+
if (config->watchdog_timer) {
844+
if (!device_is_ready(config->watchdog_timer)) {
845+
LOG_ERR("Watchdog timer is not ready");
846+
return -EINVAL;
847+
}
848+
849+
data->watchdog_timer_cfg.user_data = (void *)data;
850+
data->watchdog_timer_cfg.callback = i2c_mspm0g3xxx_target_reset;
851+
data->watchdog_timer_cfg.ticks = counter_us_to_ticks(
852+
config->watchdog_timer, CONFIG_I2C_MSPM0G3XXX_WATCHDOG_TIMEOUT);
853+
}
854+
777855
/* Init power */
778856
DL_I2C_reset((I2C_Regs *)config->base);
779857
DL_I2C_enablePower((I2C_Regs *)config->base);
@@ -895,6 +973,7 @@ static const struct i2c_driver_api i2c_mspm0g3xxx_driver_api = {
895973
.divideRatio = DL_I2C_CLOCK_DIVIDE_1}, \
896974
.dt_bitrate = CALC_DT_BITRATE(DT_INST_PROP(index, clock_frequency), \
897975
DT_INST_PROP_BY_PHANDLE(index, clocks, clock_frequency)), \
976+
.watchdog_timer = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(DT_DRV_INST(index), watchdog_timer)),\
898977
}; \
899978
\
900979
static struct i2c_mspm0g3xxx_data i2c_mspm0g3xxx_data_##index = { \
@@ -903,7 +982,7 @@ static const struct i2c_driver_api i2c_mspm0g3xxx_driver_api = {
903982
\
904983
I2C_DEVICE_DT_INST_DEFINE(index, i2c_mspm0g3xxx_init, NULL, &i2c_mspm0g3xxx_data_##index, \
905984
&i2c_mspm0g3xxx_cfg_##index, POST_KERNEL, \
906-
CONFIG_I2C_INIT_PRIORITY, &i2c_mspm0g3xxx_driver_api); \
985+
CONFIG_I2C_MSPM0G3XXX_INIT_PRIORITY, &i2c_mspm0g3xxx_driver_api); \
907986
\
908987
INTERRUPT_INIT_FUNCTION(index)
909988

0 commit comments

Comments
 (0)