Skip to content

Commit

Permalink
risc-v/esp32c3: Release stuck I2C slaves on Reset
Browse files Browse the repository at this point in the history
  • Loading branch information
gustavonihei authored and acassis committed Mar 11, 2021
1 parent cb1c415 commit d87274c
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/risc-v/src/esp32c3/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ config ESP32C3_ESP32C3XXX
bool
default n
select ESP32C3_SINGLE_CPU
select ARCH_HAVE_I2CRESET

config ESP32C3_FLASH_2M
bool
Expand Down
108 changes: 107 additions & 1 deletion arch/risc-v/src/esp32c3/esp32c3_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@

#define I2C_FILTER_CYC_NUM_DEF (7)

/* Number of bus cycles for the master to generate when the slave is in
* deadlock
*/

#define I2C_SCL_CYC_NUM_DEF (9)

/* I2C default clock frequency */

#define I2C_CLK_FREQ_DEF (100 * 1000)
Expand Down Expand Up @@ -740,6 +746,7 @@ static void esp32c3_i2c_deinit(struct esp32c3_i2c_priv_s *priv)
static void esp32c3_i2c_reset_fsmc(struct esp32c3_i2c_priv_s *priv)
{
esp32c3_i2c_deinit(priv);

esp32c3_i2c_init(priv);
}

Expand Down Expand Up @@ -951,6 +958,101 @@ static int esp32c3_i2c_transfer(struct i2c_master_s *dev,
return ret;
}

/****************************************************************************
* Name: esp32c3_i2c_clear_bus
*
* Description:
* Clear I2C bus, when the slave is stuck in a deadlock and keeps pulling
* the bus low, master can control the SCL bus to generate 9 CLKs.
*
* Parameters:
* priv - Pointer to the internal driver state structure.
*
****************************************************************************/

#ifdef CONFIG_I2C_RESET
static void esp32c3_i2c_clear_bus(struct esp32c3_i2c_priv_s *priv)
{
uint32_t clock_count;
uint32_t stretch_count;
int ret;

const struct esp32c3_i2c_config_s *config = priv->config;

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

esp32c3_configgpio(config->scl_pin, INPUT_PULLUP | OUTPUT_OPEN_DRAIN);
esp32c3_gpio_matrix_out(config->scl_pin, SIG_GPIO_OUT_IDX, 0, 0);
esp32c3_configgpio(config->sda_pin, INPUT_PULLUP | OUTPUT_OPEN_DRAIN);
esp32c3_gpio_matrix_out(config->sda_pin, SIG_GPIO_OUT_IDX, 0, 0);

/* Let SDA go high */

esp32c3_gpiowrite(config->sda_pin, 1);

/* Clock the bus until any slaves currently driving it let it go. */

clock_count = 0;
while (!esp32c3_gpioread(config->sda_pin))
{
/* Give up if we have tried too hard */

if (clock_count++ >= I2C_SCL_CYC_NUM_DEF)
{
ret = -EIO;
goto out;
}

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

stretch_count = 0;
while (!esp32c3_gpioread(config->scl_pin))
{
/* Give up if we have tried too hard */

if (stretch_count++ > 10)
{
ret = -EIO;
goto out;
}

up_udelay(10);
}

/* Drive SCL low */

esp32c3_gpiowrite(config->scl_pin, 0);
up_udelay(10);

/* Drive SCL high again */

esp32c3_gpiowrite(config->scl_pin, 1);
up_udelay(10);
}

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

esp32c3_gpiowrite(config->sda_pin, 0);
up_udelay(10);
esp32c3_gpiowrite(config->scl_pin, 0);
up_udelay(10);
esp32c3_gpiowrite(config->scl_pin, 1);
up_udelay(10);
esp32c3_gpiowrite(config->sda_pin, 1);
up_udelay(10);

ret = OK;

out:
return ret;
}
#endif

/****************************************************************************
* Name: esp32c3_i2c_reset
*
Expand Down Expand Up @@ -978,7 +1080,11 @@ static int esp32c3_i2c_reset(struct i2c_master_s *dev)

flags = enter_critical_section();

esp32c3_i2c_reset_fsmc(priv);
esp32c3_i2c_deinit(priv);

esp32c3_i2c_clear_bus(priv);

esp32c3_i2c_init(priv);

priv->i2cstate = I2CSTATE_IDLE;
priv->msgid = 0;
Expand Down

0 comments on commit d87274c

Please sign in to comment.