diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a81ce59bc977a..2fe7b589b74cc 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -635,6 +635,7 @@ config ARCH_CHIP_CXD56XX select ARCH_HAVE_TEXT_HEAP select ARCH_HAVE_SDIO if MMCSD select ARCH_HAVE_MATH_H + select ARCH_HAVE_I2CRESET ---help--- Sony CXD56XX (ARM Cortex-M4) architectures diff --git a/arch/arm/src/cxd56xx/cxd56_i2c.c b/arch/arm/src/cxd56xx/cxd56_i2c.c index bcb517696f2a3..56eba21a96bc5 100644 --- a/arch/arm/src/cxd56xx/cxd56_i2c.c +++ b/arch/arm/src/cxd56xx/cxd56_i2c.c @@ -46,6 +46,7 @@ #include "cxd56_i2c.h" #include "hardware/cxd56_i2c.h" #include "cxd56_pinconfig.h" +#include "cxd56_gpio.h" #if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ) #include @@ -702,7 +703,124 @@ static int cxd56_i2c_transfer(struct i2c_master_s *dev, #ifdef CONFIG_I2C_RESET static int cxd56_i2c_reset(struct i2c_master_s *dev) { - return OK; + struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)dev; + unsigned int clock_count; + unsigned int stretch_count; + uint32_t scl_gpio; + uint32_t sda_gpio; + int ret = -EIO; + + DEBUGASSERT(dev != NULL); + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + nxmutex_lock(&priv->lock); + + /* Use GPIO configuration to un-wedge the bus */ + + cxd56_i2c_pincontrol(priv->port, false); + + switch (priv->port) + { + case 0: + scl_gpio = PIN_I2C0_BCK; + sda_gpio = PIN_I2C0_BDT; + break; + case 1: + scl_gpio = PIN_PWM2; + sda_gpio = PIN_PWM3; + break; + case 2: + default: + scl_gpio = PIN_SPI0_MOSI; + sda_gpio = PIN_SPI0_MISO; + break; + } + + /* Enable input of SCL and SDA pins */ + + cxd56_gpio_config(scl_gpio, true); + cxd56_gpio_config(sda_gpio, true); + + /* Let SDA go high */ + + cxd56_gpio_write(sda_gpio, true); + + /* Clock the bus until any slaves currently driving it let it go. */ + + clock_count = 0; + while (!cxd56_gpio_read(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 (!cxd56_gpio_read(scl_gpio)) + { + /* Give up if we have tried too hard */ + + if (stretch_count++ > 10) + { + goto out; + } + + up_udelay(10); + } + + /* Drive SCL low */ + + cxd56_gpio_write(scl_gpio, false); + up_udelay(10); + + /* Drive SCL high again */ + + cxd56_gpio_write(scl_gpio, true); + up_udelay(10); + } + + /* Generate a start followed by a stop to reset slave + * state machines. + */ + + cxd56_gpio_write(sda_gpio, false); + up_udelay(10); + cxd56_gpio_write(scl_gpio, false); + up_udelay(10); + cxd56_gpio_write(scl_gpio, true); + up_udelay(10); + cxd56_gpio_write(sda_gpio, true); + up_udelay(10); + + ret = OK; + +out: + + /* Disable output of SCL and SDA pins */ + + cxd56_gpio_write_hiz(scl_gpio); + cxd56_gpio_write_hiz(sda_gpio); + + /* Revert the GPIO configuration. */ + + cxd56_i2c_pincontrol(priv->port, true); + + /* Release the port for re-use by other clients */ + + nxmutex_unlock(&priv->lock); + return ret; } #endif /* CONFIG_I2C_RESET */