mirror of
https://github.com/apache/nuttx.git
synced 2026-09-12 05:30:20 +00:00
rp2040: support I2C_RESET
This commit is contained in:
parent
c109262a7f
commit
3a0cd56623
2 changed files with 162 additions and 18 deletions
|
|
@ -231,6 +231,7 @@ config ARCH_CHIP_RP2040
|
|||
select ARCH_HAVE_RAMVECTORS
|
||||
select ARCH_HAVE_MULTICPU
|
||||
select ARCH_HAVE_TESTSET
|
||||
select ARCH_HAVE_I2CRESET
|
||||
select ARM_HAVE_WFE_SEV
|
||||
---help---
|
||||
Raspberry Pi RP2040 architectures (ARM dual Cortex-M0+).
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@
|
|||
|
||||
#include "rp2040_i2c.h"
|
||||
#include "hardware/rp2040_i2c.h"
|
||||
#include "hardware/rp2040_resets.h"
|
||||
#include "rp2040_gpio.h"
|
||||
|
||||
#ifdef CONFIG_RP2040_I2C
|
||||
|
||||
|
|
@ -124,6 +126,7 @@ static inline void i2c_reg_rmw(struct rp2040_i2cdev_s *dev,
|
|||
uint32_t val, uint32_t mask);
|
||||
|
||||
static int rp2040_i2c_disable(struct rp2040_i2cdev_s *priv);
|
||||
static void rp2040_i2c_init(struct rp2040_i2cdev_s *priv);
|
||||
static void rp2040_i2c_enable(struct rp2040_i2cdev_s *priv);
|
||||
|
||||
static int rp2040_i2c_interrupt(int irq, FAR void *context, FAR void *arg);
|
||||
|
|
@ -639,7 +642,143 @@ static int rp2040_i2c_transfer(FAR struct i2c_master_s *dev,
|
|||
#ifdef CONFIG_I2C_RESET
|
||||
static int rp2040_i2c_reset(FAR struct i2c_master_s *dev)
|
||||
{
|
||||
return OK;
|
||||
FAR struct rp2040_i2cdev_s *priv = (struct rp2040_i2cdev_s *)dev;
|
||||
unsigned int clock_count;
|
||||
unsigned int stretch_count;
|
||||
uint32_t scl_gpio;
|
||||
uint32_t sda_gpio;
|
||||
uint32_t subsys;
|
||||
uint32_t frequency;
|
||||
int pin;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(dev);
|
||||
|
||||
/* Our caller must own a ref */
|
||||
|
||||
DEBUGASSERT(priv->refs > 0);
|
||||
|
||||
/* Lock out other clients */
|
||||
|
||||
i2c_takesem(&priv->mutex);
|
||||
|
||||
ret = -EIO;
|
||||
|
||||
/* De-init the port */
|
||||
|
||||
rp2040_i2c_disable(priv);
|
||||
|
||||
/* Use GPIO configuration to un-wedge the bus */
|
||||
|
||||
pin = rp2040_gpio_get_function_pin(RP2040_GPIO_FUNC_I2C, priv->port);
|
||||
if (pin < 0)
|
||||
{
|
||||
goto out_without_reinit;
|
||||
}
|
||||
|
||||
sda_gpio = pin;
|
||||
scl_gpio = pin + 1;
|
||||
subsys = (priv->port == 0) ? RP2040_RESETS_RESET_I2C0 :
|
||||
RP2040_RESETS_RESET_I2C1;
|
||||
|
||||
rp2040_gpio_init(sda_gpio);
|
||||
rp2040_gpio_setdir(sda_gpio, true);
|
||||
rp2040_gpio_set_pulls(sda_gpio, true, false); /* Pull up */
|
||||
rp2040_gpio_put(sda_gpio, true);
|
||||
|
||||
rp2040_gpio_init(scl_gpio);
|
||||
rp2040_gpio_setdir(scl_gpio, true);
|
||||
rp2040_gpio_set_pulls(scl_gpio, true, false);
|
||||
rp2040_gpio_put(scl_gpio, true);
|
||||
|
||||
/* Let SDA go high */
|
||||
|
||||
rp2040_gpio_put(sda_gpio, true);
|
||||
|
||||
/* Clock the bus until any slaves currently driving it let it go. */
|
||||
|
||||
clock_count = 0;
|
||||
while (!rp2040_gpio_get(sda_gpio))
|
||||
{
|
||||
/* 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 (!rp2040_gpio_get(scl_gpio))
|
||||
{
|
||||
/* Give up if we have tried too hard */
|
||||
|
||||
if (stretch_count++ > 10)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
up_udelay(10);
|
||||
}
|
||||
|
||||
/* Drive SCL low */
|
||||
|
||||
rp2040_gpio_put(scl_gpio, false);
|
||||
up_udelay(10);
|
||||
|
||||
/* Drive SCL high again */
|
||||
|
||||
rp2040_gpio_put(scl_gpio, true);
|
||||
up_udelay(10);
|
||||
}
|
||||
|
||||
/* Generate a start followed by a stop to reset slave state machines. */
|
||||
|
||||
rp2040_gpio_put(sda_gpio, false);
|
||||
up_udelay(10);
|
||||
rp2040_gpio_put(scl_gpio, false);
|
||||
up_udelay(10);
|
||||
rp2040_gpio_put(scl_gpio, true);
|
||||
up_udelay(10);
|
||||
rp2040_gpio_put(sda_gpio, true);
|
||||
up_udelay(10);
|
||||
|
||||
ret = OK;
|
||||
|
||||
out:
|
||||
|
||||
/* Revert the GPIO configuration. */
|
||||
|
||||
rp2040_gpio_set_function(sda_gpio, RP2040_GPIO_FUNC_I2C);
|
||||
rp2040_gpio_set_function(scl_gpio, RP2040_GPIO_FUNC_I2C);
|
||||
|
||||
/* Reset I2C subsystem */
|
||||
|
||||
setbits_reg32(subsys, RP2040_RESETS_RESET);
|
||||
clrbits_reg32(subsys, RP2040_RESETS_RESET);
|
||||
while ((getreg32(RP2040_RESETS_RESET_DONE) & subsys) == 0)
|
||||
;
|
||||
|
||||
/* Re-init the port */
|
||||
|
||||
rp2040_i2c_disable(priv);
|
||||
rp2040_i2c_init(priv);
|
||||
|
||||
/* Restore the frequency */
|
||||
|
||||
frequency = priv->frequency;
|
||||
priv->frequency = 0;
|
||||
rp2040_i2c_setfrequency(priv, frequency);
|
||||
|
||||
out_without_reinit:
|
||||
|
||||
/* Release the port for re-use by other clients */
|
||||
|
||||
i2c_givesem(&priv->mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_I2C_RESET */
|
||||
|
||||
|
|
@ -694,6 +833,26 @@ static int rp2040_i2c_disable(struct rp2040_i2cdev_s *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rp2040_i2c_init(struct rp2040_i2cdev_s *priv)
|
||||
{
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, 0x00);
|
||||
i2c_reg_read(priv, RP2040_I2C_IC_CLR_INTR_OFFSET);
|
||||
|
||||
/* set threshold level of the Rx/Tx FIFO */
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_RX_TL_OFFSET, 0xff);
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_TX_TL_OFFSET, 0);
|
||||
|
||||
/* set hold time for margin */
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_SDA_HOLD_OFFSET, 1);
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_CON_OFFSET,
|
||||
(RP2040_I2C_IC_CON_IC_SLAVE_DISABLE |
|
||||
RP2040_I2C_IC_CON_MASTER_MODE |
|
||||
RP2040_I2C_IC_CON_TX_EMPTY_CTRL));
|
||||
}
|
||||
|
||||
static void rp2040_i2c_enable(struct rp2040_i2cdev_s *priv)
|
||||
{
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, I2C_INTR_ENABLE);
|
||||
|
|
@ -758,23 +917,7 @@ struct i2c_master_s *rp2040_i2cbus_initialize(int port)
|
|||
priv->base_freq = BOARD_PERI_FREQ;
|
||||
|
||||
rp2040_i2c_disable(priv);
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_INTR_MASK_OFFSET, 0x00);
|
||||
i2c_reg_read(priv, RP2040_I2C_IC_CLR_INTR_OFFSET);
|
||||
|
||||
/* set threshold level of the Rx/Tx FIFO */
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_RX_TL_OFFSET, 0xff);
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_TX_TL_OFFSET, 0);
|
||||
|
||||
/* set hold time for margin */
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_SDA_HOLD_OFFSET, 1);
|
||||
|
||||
i2c_reg_write(priv, RP2040_I2C_IC_CON_OFFSET,
|
||||
(RP2040_I2C_IC_CON_IC_SLAVE_DISABLE |
|
||||
RP2040_I2C_IC_CON_MASTER_MODE |
|
||||
RP2040_I2C_IC_CON_TX_EMPTY_CTRL));
|
||||
rp2040_i2c_init(priv);
|
||||
rp2040_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY);
|
||||
|
||||
leave_critical_section(flags);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue