drivers: i2c: nrfx_twim: disable and restore state for bus recovery

TWIM peripheral needs to be disabled for bus recovery, otherwise SCL/SDA
lines will not be released. This patch makes sure to disable peripheral
if active, and, restore its state afterwards (including pin
configuration).

It is worth to note that a better solution would be to:

1. Define scl/sda pins as `-gpios` in DT
2. Use GPIO API in the driver to perform recovery (as some other drivers
   do)
3. Potentially use a "gpio" pinctrl state for this case

Unfortunately HAL is doing everything under the hood, so we have little
options to improve this unless we don't use it for such case. GPIO based
recovery should likely be generalized as many drivers seem to replicate
such _algorithm_.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2022-07-12 11:15:27 +02:00 committed by Fabio Baltieri
parent 2828578c25
commit 9974bb043f

View file

@ -169,7 +169,6 @@ static int i2c_nrfx_twim_transfer(const struct device *dev,
* bus from this error. * bus from this error.
*/ */
LOG_ERR("Error on I2C line occurred for message %d", i); LOG_ERR("Error on I2C line occurred for message %d", i);
nrfx_twim_disable(&dev_config->twim);
(void)i2c_nrfx_twim_recover_bus(dev); (void)i2c_nrfx_twim_recover_bus(dev);
ret = -EIO; ret = -EIO;
break; break;
@ -266,13 +265,13 @@ static int i2c_nrfx_twim_configure(const struct device *dev,
static int i2c_nrfx_twim_recover_bus(const struct device *dev) static int i2c_nrfx_twim_recover_bus(const struct device *dev)
{ {
const struct i2c_nrfx_twim_config *dev_config = dev->config;
enum pm_device_state state;
uint32_t scl_pin; uint32_t scl_pin;
uint32_t sda_pin; uint32_t sda_pin;
nrfx_err_t err; nrfx_err_t err;
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
const struct i2c_nrfx_twim_config *dev_config = dev->config;
scl_pin = nrf_twim_scl_pin_get(dev_config->twim.p_twim); scl_pin = nrf_twim_scl_pin_get(dev_config->twim.p_twim);
sda_pin = nrf_twim_sda_pin_get(dev_config->twim.p_twim); sda_pin = nrf_twim_sda_pin_get(dev_config->twim.p_twim);
#else #else
@ -282,7 +281,21 @@ static int i2c_nrfx_twim_recover_bus(const struct device *dev)
sda_pin = dev_data->twim_config.sda; sda_pin = dev_data->twim_config.sda;
#endif #endif
/* disable peripheral if active (required to release SCL/SDA lines) */
(void)pm_device_state_get(dev, &state);
if (state == PM_DEVICE_STATE_ACTIVE) {
nrfx_twim_disable(&dev_config->twim);
}
err = nrfx_twim_bus_recover(scl_pin, sda_pin); err = nrfx_twim_bus_recover(scl_pin, sda_pin);
/* restore peripheral if it was active before */
if (state == PM_DEVICE_STATE_ACTIVE) {
(void)pinctrl_apply_state(dev_config->pcfg,
PINCTRL_STATE_DEFAULT);
nrfx_twim_enable(&dev_config->twim);
}
return (err == NRFX_SUCCESS ? 0 : -EBUSY); return (err == NRFX_SUCCESS ? 0 : -EBUSY);
} }