Skip to content

Commit

Permalink
arch/arm64/src/imx9/imx9_lpi2c.c: Fix bus reset
Browse files Browse the repository at this point in the history
imx9_lpi2c_reset didn't quite work. It tried to toggle scl line as GPIO, but the line was not even muxed properly.

Instead, set the bus to relaxed mode, and drive address 0 to the bus. This toggles the clock, and also generates start and stop sequences on the bus.

"Relaxed mode" is a new feature on the LPI2C block used in imx93.

Signed-off-by: Jukka Laitinen <[email protected]>
  • Loading branch information
jlaitine authored and pussuw committed Nov 28, 2024
1 parent 459acb1 commit 890058d
Showing 1 changed file with 23 additions and 115 deletions.
138 changes: 23 additions & 115 deletions arch/arm64/src/imx9/imx9_lpi2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,6 @@ struct imx9_lpi2c_config_s
uint8_t filtsda; /* Glitch Filter for SDA pin */
iomux_cfg_t scl_pin; /* Peripheral configuration for SCL as SCL */
iomux_cfg_t sda_pin; /* Peripheral configuration for SDA as SDA */
#if defined(CONFIG_I2C_RESET)
gpio_pinset_t reset_scl_pin; /* GPIO configuration for SCL as GPIO */
gpio_pinset_t reset_sda_pin; /* GPIO configuration for SDA as GPIO */
#endif
uint8_t mode; /* Master or Slave mode */
#ifndef CONFIG_I2C_POLLED
uint32_t irq; /* Event IRQ */
Expand Down Expand Up @@ -338,10 +334,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c1_config =
.filtsda = CONFIG_IMX9_LPI2C1_FILTSDA,
.scl_pin = MUX_LPI2C1_SCL,
.sda_pin = MUX_LPI2C1_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C1_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C1_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -386,10 +378,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c2_config =
.filtsda = CONFIG_IMX9_LPI2C2_FILTSDA,
.scl_pin = MUX_LPI2C2_SCL,
.sda_pin = MUX_LPI2C2_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C2_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C2_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -434,10 +422,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c3_config =
.filtsda = CONFIG_IMX9_LPI2C3_FILTSDA,
.scl_pin = MUX_LPI2C3_SCL,
.sda_pin = MUX_LPI2C3_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C3_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C3_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -482,10 +466,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c4_config =
.filtsda = CONFIG_IMX9_LPI2C4_FILTSDA,
.scl_pin = MUX_LPI2C4_SCL,
.sda_pin = MUX_LPI2C4_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C4_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C4_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -530,10 +510,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c5_config =
.filtsda = CONFIG_IMX9_LPI2C5_FILTSDA,
.scl_pin = MUX_LPI2C5_SCL,
.sda_pin = MUX_LPI2C5_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C5_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C5_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -578,10 +554,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c6_config =
.filtsda = CONFIG_IMX9_LPI2C6_FILTSDA,
.scl_pin = MUX_LPI2C6_SCL,
.sda_pin = MUX_LPI2C6_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C6_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C6_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -626,10 +598,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c7_config =
.filtsda = CONFIG_IMX9_LPI2C7_FILTSDA,
.scl_pin = MUX_LPI2C7_SCL,
.sda_pin = MUX_LPI2C7_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C7_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C7_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -674,10 +642,6 @@ static const struct imx9_lpi2c_config_s imx9_lpi2c8_config =
.filtsda = CONFIG_IMX9_LPI2C8_FILTSDA,
.scl_pin = MUX_LPI2C8_SCL,
.sda_pin = MUX_LPI2C8_SDA,
#if defined(CONFIG_I2C_RESET)
.reset_scl_pin = GPIO_LPI2C8_SCL_RESET,
.reset_sda_pin = GPIO_LPI2C8_SDA_RESET,
#endif
#ifndef CONFIG_I2C_SLAVE
.mode = LPI2C_MASTER,
#else
Expand Down Expand Up @@ -2187,11 +2151,8 @@ static int imx9_lpi2c_transfer(struct i2c_master_s *dev,
static int imx9_lpi2c_reset(struct i2c_master_s *dev)
{
struct imx9_lpi2c_priv_s *priv = (struct imx9_lpi2c_priv_s *)dev;
unsigned int clock_count;
unsigned int stretch_count;
uint32_t scl_gpio;
uint32_t sda_gpio;
uint32_t frequency;
uint32_t status;
unsigned count;
int ret;

DEBUGASSERT(dev);
Expand All @@ -2208,95 +2169,42 @@ static int imx9_lpi2c_reset(struct i2c_master_s *dev)
return ret;
}

ret = -EIO;

/* Save the current frequency */

frequency = priv->frequency;

/* De-init the port */

imx9_lpi2c_deinit(priv);

/* Use GPIO configuration to un-wedge the bus */

imx9_iomux_gpio(priv->config->scl_pin, true);
imx9_iomux_gpio(priv->config->sda_pin, true);

scl_gpio = priv->config->reset_scl_pin;
sda_gpio = priv->config->reset_sda_pin;
/* Re-init the port */

imx9_config_gpio(scl_gpio);
imx9_config_gpio(sda_gpio);
imx9_lpi2c_init(priv);

/* Let SDA go high */
/* Set bus to relaxed mode - this allows clocking the bus even when it
* is busy
*/

imx9_gpio_write(sda_gpio, 1);
imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR0_OFFSET,
0, LPI2C_MCFG0_RELAX);

/* Clock the bus until any slaves currently driving it let it go. */
/* Clock the bus until it gets freed (max 10 times) */

clock_count = 0;
while (!imx9_gpio_read(sda_gpio))
count = 0;
status = imx9_lpi2c_getstatus(priv);
while ((status & LPI2C_MSR_BBF) != 0 && count++ < 10)
{
/* Give up if we have tried too hard */

if (clock_count++ > 10)
{
goto out;
}

/* Sniff to make sure that clock stretching has finished.
*
* If the bus never relaxes, the reset has failed.
*/

stretch_count = 0;
while (!imx9_gpio_read(scl_gpio))
{
/* Give up if we have tried too hard */

if (stretch_count++ > 10)
{
goto out;
}

up_udelay(10);
}

/* Drive SCL low */

imx9_gpio_write(scl_gpio, 0);
up_udelay(10);

/* Drive SCL high again */

imx9_gpio_write(scl_gpio, 1);
up_udelay(10);
i2cerr("Bus %d busy!\n", priv->config->clk_root - CCM_CR_LPI2C1 + 1);
imx9_lpi2c_sendstart(priv, 0);
imx9_lpi2c_sendstop(priv);
usleep(1000);
status = imx9_lpi2c_getstatus(priv);
}

/* Generate a start followed by a stop to reset slave
* state machines.
*/

imx9_gpio_write(sda_gpio, 0);
up_udelay(10);
imx9_gpio_write(scl_gpio, 0);
up_udelay(10);
imx9_gpio_write(scl_gpio, 1);
up_udelay(10);
imx9_gpio_write(sda_gpio, 1);
up_udelay(10);
/* Check that the bus is now free */

/* Re-init the port */

imx9_lpi2c_init(priv);
ret = (status & LPI2C_MSR_BBF) == 0 ? OK : -EIO;

/* Restore the frequency */
/* Exit relaxed mode */

imx9_lpi2c_setclock(priv, frequency);
ret = OK;

out:
imx9_lpi2c_modifyreg(priv, IMX9_LPI2C_MCFGR0_OFFSET,
LPI2C_MCFG0_RELAX, 0);

/* Release the port for re-use by other clients */

Expand Down

0 comments on commit 890058d

Please sign in to comment.