Skip to content

Commit 8302474

Browse files
malto101kartben
authored andcommitted
drivers: i2c: Added Bus recovery support to OMAP I2C.
The bus recovery feature utilizing bit-bang operations, which sends SCL clock pulses to recover the bus and checks if the line is down. Upon recovery, it disables the system test register before resuming the transactions. Signed-off-by: Dhruv Menon <dhruvmenon1104@gmail.com>
1 parent 1d8ea45 commit 8302474

File tree

2 files changed

+145
-26
lines changed

2 files changed

+145
-26
lines changed

drivers/i2c/Kconfig.omap

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ config I2C_OMAP
77
bool "TI OMAP I2C Driver"
88
default y
99
depends on DT_HAS_TI_OMAP_I2C_ENABLED
10+
help
11+
Enable the I2C driver for TI OMAP SoCs.
12+
13+
config I2C_OMAP_BUS_RECOVERY
14+
bool "Bus recovery support"
15+
depends on I2C_OMAP
1016
select I2C_BITBANG
1117
help
12-
Enable the I2C driver for TI OMAP SoCs.
18+
Enable OMAP I2C driver bus recovery support via bitbanging.

drivers/i2c/i2c_omap.c

Lines changed: 138 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#include <zephyr/irq.h>
1414
#include <zephyr/drivers/i2c.h>
1515

16+
#ifdef CONFIG_I2C_OMAP_BUS_RECOVERY
17+
#include "i2c_bitbang.h"
18+
#endif /* CONFIG_I2C_OMAP_BUS_RECOVERY */
19+
1620
LOG_MODULE_REGISTER(omap_i2c, CONFIG_I2C_LOG_LEVEL);
1721

1822
#define I2C_OMAP_TIMEOUT 100U
@@ -216,30 +220,6 @@ static int i2c_omap_set_speed(const struct device *dev, uint32_t speed)
216220
return 0;
217221
}
218222

219-
/**
220-
* @brief Initialize the OMAP I2C controller.
221-
*
222-
* This function initializes the OMAP I2C controller by setting the speed and
223-
* performing any necessary initialization steps.
224-
*
225-
* @param dev Pointer to the device structure for the I2C controller.
226-
* @return 0 if successful, negative error code otherwise.
227-
*/
228-
static int i2c_omap_init(const struct device *dev)
229-
{
230-
struct i2c_omap_data *data = DEV_DATA(dev);
231-
const struct i2c_omap_cfg *cfg = DEV_CFG(dev);
232-
k_sem_init(&data->lock, 1, 1);
233-
234-
/* Set the speed for I2C */
235-
if (i2c_omap_set_speed(dev, cfg->speed)) {
236-
LOG_ERR("Failed to set speed");
237-
return -ENOTSUP;
238-
}
239-
i2c_omap_init_ll(dev);
240-
return 0;
241-
}
242-
243223
/**
244224
* @brief Configure the OMAP I2C controller with the specified device configuration.
245225
*
@@ -322,12 +302,113 @@ static void i2c_omap_resize_fifo(const struct device *dev, uint8_t size)
322302
}
323303
}
324304

305+
#ifdef CONFIG_I2C_OMAP_BUS_RECOVERY
306+
/**
307+
* @brief Get the state of the SDA line.
308+
*
309+
* This function retrieves the state of the SDA (data) line for the OMAP I2C controller.
310+
*
311+
* @param io_context The I2C context.
312+
* @return The state of the SDA line.
313+
*/
314+
static int i2c_omap_get_sda(void *io_context)
315+
{
316+
const struct i2c_omap_cfg *cfg = (const struct i2c_omap_cfg *)io_context;
317+
i2c_omap_regs_t *i2c_base_addr = (i2c_omap_regs_t *)cfg->base.addr;
318+
319+
return (i2c_base_addr->SYSTEST & I2C_OMAP_SYSTEST_SDA_I_FUNC) ? 1 : 0;
320+
}
321+
322+
/**
323+
* @brief Set the state of the SDA line.
324+
*
325+
* This function sets the state of the SDA (data) line for the OMAP I2C controller.
326+
*
327+
* @param io_context The I2C context.
328+
* @param state The state to set (0 for low, 1 for high).
329+
*/
330+
static void i2c_omap_set_sda(void *io_context, int state)
331+
{
332+
const struct i2c_omap_cfg *cfg = (const struct i2c_omap_cfg *)io_context;
333+
i2c_omap_regs_t *i2c_base_addr = (i2c_omap_regs_t *)cfg->base.addr;
334+
335+
if (state) {
336+
i2c_base_addr->SYSTEST |= I2C_OMAP_SYSTEST_SDA_O;
337+
} else {
338+
i2c_base_addr->SYSTEST &= ~I2C_OMAP_SYSTEST_SDA_O;
339+
}
340+
}
341+
342+
/**
343+
* @brief Set the state of the SCL line.
344+
*
345+
* This function sets the state of the SCL (clock) line for the OMAP I2C controller.
346+
*
347+
* @param io_context The I2C context.
348+
* @param state The state to set (0 for low, 1 for high).
349+
*/
350+
static void i2c_omap_set_scl(void *io_context, int state)
351+
{
352+
const struct i2c_omap_cfg *cfg = (const struct i2c_omap_cfg *)io_context;
353+
i2c_omap_regs_t *i2c_base_addr = (i2c_omap_regs_t *)cfg->base.addr;
354+
355+
if (state) {
356+
i2c_base_addr->SYSTEST |= I2C_OMAP_SYSTEST_SCL_O;
357+
} else {
358+
i2c_base_addr->SYSTEST &= ~I2C_OMAP_SYSTEST_SCL_O;
359+
}
360+
}
361+
/**
362+
* @brief Recovers the I2C bus using the OMAP I2C controller.
363+
*
364+
* This function attempts to recover the I2C bus by performing a bus recovery
365+
* sequence using the OMAP I2C controller. It uses the provided device
366+
* configuration and bit-banging operations to recover the bus.
367+
*
368+
* @param dev Pointer to the device structure.
369+
* @return 0 on success, negative error code on failure.
370+
*/
371+
372+
static int i2c_omap_recover_bus(const struct device *dev)
373+
{
374+
const struct i2c_omap_cfg *cfg = DEV_CFG(dev);
375+
i2c_omap_regs_t *i2c_base_addr = DEV_I2C_BASE(dev);
376+
struct i2c_omap_data *data = DEV_DATA(dev);
377+
378+
struct i2c_bitbang bitbang_omap;
379+
struct i2c_bitbang_io bitbang_omap_io = {
380+
.get_sda = i2c_omap_get_sda,
381+
.set_scl = i2c_omap_set_scl,
382+
.set_sda = i2c_omap_set_sda,
383+
};
384+
int error = 0;
385+
386+
k_sem_take(&data->lock, K_FOREVER);
387+
i2c_base_addr->SYSTEST |= I2C_OMAP_SYSTEST_ST_EN | (3 << I2C_OMAP_SYSTEST_TMODE_SHIFT) |
388+
I2C_OMAP_SYSTEST_SCL_O | I2C_OMAP_SYSTEST_SDA_O;
389+
i2c_bitbang_init(&bitbang_omap, &bitbang_omap_io, (void *)cfg);
390+
error = i2c_bitbang_recover_bus(&bitbang_omap);
391+
if (error != 0) {
392+
LOG_ERR("failed to recover bus (err %d)", error);
393+
goto restore;
394+
}
395+
396+
restore:
397+
i2c_base_addr->SYSTEST &= ~(I2C_OMAP_SYSTEST_ST_EN | I2C_OMAP_SYSTEST_TMODE_MASK |
398+
I2C_OMAP_SYSTEST_SCL_O | I2C_OMAP_SYSTEST_SDA_O);
399+
i2c_omap_reset(dev);
400+
k_sem_give(&data->lock);
401+
return error;
402+
}
403+
#endif /* CONFIG_I2C_OMAP_BUS_RECOVERY */
404+
325405
/**
326406
* @brief Wait for the bus to become free (no longer busy).
327407
*
328408
* This function waits for the bus to become free by continuously checking the
329409
* status register of the OMAP I2C controller. If the bus remains busy for a
330-
* certain timeout period, the function will return timeout error.
410+
* certain timeout period, the function will return attempts to recover the bus by calling
411+
* i2c_omap_recover_bus().
331412
*
332413
* @param dev The I2C device structure.
333414
* @return 0 if the bus becomes free, or a negative error code if the bus cannot
@@ -341,6 +422,11 @@ static int i2c_omap_wait_for_bb(const struct device *dev)
341422
while (i2c_base_addr->STAT & I2C_OMAP_STAT_BB) {
342423
if (k_uptime_get_32() > timeout) {
343424
LOG_ERR("Bus busy timeout");
425+
#ifdef CONFIG_I2C_OMAP_BUS_RECOVERY
426+
return i2c_omap_recover_bus(dev);
427+
#else
428+
return -ETIMEDOUT;
429+
#endif /* CONFIG_I2C_OMAP_BUS_RECOVERY */
344430
}
345431
k_busy_wait(100);
346432
}
@@ -576,8 +662,35 @@ static int i2c_omap_transfer_polling(const struct device *dev, struct i2c_msg ms
576662
static DEVICE_API(i2c, i2c_omap_api) = {
577663
.transfer = i2c_omap_transfer_polling,
578664
.configure = i2c_omap_configure,
665+
#ifdef CONFIG_I2C_OMAP_BUS_RECOVERY
666+
.recover_bus = i2c_omap_recover_bus,
667+
#endif /* CONFIG_I2C_OMAP_BUS_RECOVERY */
579668
};
580669

670+
/**
671+
* @brief Initialize the OMAP I2C controller.
672+
*
673+
* This function initializes the OMAP I2C controller by setting the speed and
674+
* performing any necessary initialization steps.
675+
*
676+
* @param dev Pointer to the device structure for the I2C controller.
677+
* @return 0 if successful, negative error code otherwise.
678+
*/
679+
static int i2c_omap_init(const struct device *dev)
680+
{
681+
struct i2c_omap_data *data = DEV_DATA(dev);
682+
const struct i2c_omap_cfg *cfg = DEV_CFG(dev);
683+
684+
k_sem_init(&data->lock, 1, 1);
685+
/* Set the speed for I2C */
686+
if (i2c_omap_set_speed(dev, cfg->speed)) {
687+
LOG_ERR("Failed to set speed");
688+
return -ENOTSUP;
689+
}
690+
i2c_omap_init_ll(dev);
691+
return 0;
692+
}
693+
581694
#define I2C_OMAP_INIT(inst) \
582695
LOG_INSTANCE_REGISTER(omap_i2c, inst, CONFIG_I2C_LOG_LEVEL); \
583696
static const struct i2c_omap_cfg i2c_omap_cfg_##inst = { \

0 commit comments

Comments
 (0)