ITE: driver/i2c: add I2C recovery function
Use GPIO output high and low to simulate I2C start and stop conditions to restore i2c to normal. Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
This commit is contained in:
parent
8fdcc11d6b
commit
abe27c5bca
3 changed files with 73 additions and 3 deletions
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#define DT_DRV_COMPAT ite_it8xxx2_i2c
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <drivers/pinmux.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -52,6 +53,8 @@ struct i2c_it8xxx2_config {
|
|||
/* Alternate function */
|
||||
uint8_t clk_alt_fun;
|
||||
uint8_t data_alt_fun;
|
||||
/* GPIO handle */
|
||||
const struct device *gpio_dev;
|
||||
};
|
||||
|
||||
enum i2c_ch_status {
|
||||
|
|
@ -766,8 +769,8 @@ static int i2c_it8xxx2_transfer(const struct device *dev, struct i2c_msg *msgs,
|
|||
if (data->i2ccs == I2C_CH_NORMAL) {
|
||||
/* Make sure we're in a good state to start */
|
||||
if (i2c_bus_not_available(dev)) {
|
||||
/* reset i2c port */
|
||||
i2c_reset(dev);
|
||||
/* Recovery I2C bus */
|
||||
i2c_recover_bus(dev);
|
||||
printk("I2C ch%d reset cause %d\n", config->port,
|
||||
I2C_RC_NO_IDLE_FOR_START);
|
||||
/*
|
||||
|
|
@ -936,9 +939,64 @@ static int i2c_it8xxx2_init(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_it8xxx2_recover_bus(const struct device *dev)
|
||||
{
|
||||
const struct i2c_it8xxx2_config *config = DEV_CFG(dev);
|
||||
int i;
|
||||
|
||||
/* Set clock of I2C as GPIO pin */
|
||||
pinmux_pin_input_enable(config->clk_pinctrls, config->clk_pin,
|
||||
PINMUX_OUTPUT_ENABLED);
|
||||
/* Set data of I2C as GPIO pin */
|
||||
pinmux_pin_input_enable(config->data_pinctrls, config->data_pin,
|
||||
PINMUX_OUTPUT_ENABLED);
|
||||
|
||||
gpio_pin_set(config->gpio_dev, config->clk_pin, 1);
|
||||
gpio_pin_set(config->gpio_dev, config->data_pin, 1);
|
||||
k_msleep(1);
|
||||
|
||||
/* Start condition */
|
||||
gpio_pin_set(config->gpio_dev, config->data_pin, 0);
|
||||
k_msleep(1);
|
||||
gpio_pin_set(config->gpio_dev, config->clk_pin, 0);
|
||||
k_msleep(1);
|
||||
|
||||
/* 9 cycles of SCL with SDA held high */
|
||||
for (i = 0; i < 9; i++) {
|
||||
gpio_pin_set(config->gpio_dev, config->data_pin, 1);
|
||||
gpio_pin_set(config->gpio_dev, config->clk_pin, 1);
|
||||
k_msleep(1);
|
||||
gpio_pin_set(config->gpio_dev, config->clk_pin, 0);
|
||||
k_msleep(1);
|
||||
}
|
||||
gpio_pin_set(config->gpio_dev, config->data_pin, 0);
|
||||
k_msleep(1);
|
||||
|
||||
/* Stop condition */
|
||||
gpio_pin_set(config->gpio_dev, config->clk_pin, 1);
|
||||
k_msleep(1);
|
||||
gpio_pin_set(config->gpio_dev, config->data_pin, 1);
|
||||
k_msleep(1);
|
||||
|
||||
/* Set GPIO back to I2C alternate function of clock */
|
||||
pinmux_pin_set(config->clk_pinctrls, config->clk_pin,
|
||||
config->clk_alt_fun);
|
||||
/* Set GPIO back to I2C alternate function of data */
|
||||
pinmux_pin_set(config->data_pinctrls, config->data_pin,
|
||||
config->data_alt_fun);
|
||||
|
||||
/* reset i2c port */
|
||||
i2c_reset(dev);
|
||||
printk("I2C ch%d reset cause %d\n", config->port,
|
||||
I2C_RC_NO_IDLE_FOR_START);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_driver_api i2c_it8xxx2_driver_api = {
|
||||
.configure = i2c_it8xxx2_configure,
|
||||
.transfer = i2c_it8xxx2_transfer,
|
||||
.recover_bus = i2c_it8xxx2_recover_bus,
|
||||
};
|
||||
|
||||
#define I2C_ITE_IT8XXX2_INIT(idx) \
|
||||
|
|
@ -955,7 +1013,8 @@ static const struct i2c_driver_api i2c_it8xxx2_driver_api = {
|
|||
.clk_pin = DEV_CLK_PIN(idx), \
|
||||
.data_pin = DEV_DATA_PIN(idx), \
|
||||
.clk_alt_fun = DEV_CLK_ALT_FUNC(idx), \
|
||||
.data_alt_fun = DEV_DATA_ALT_FUNC(idx) \
|
||||
.data_alt_fun = DEV_DATA_ALT_FUNC(idx), \
|
||||
.gpio_dev = DEVICE_DT_GET(DT_INST_PHANDLE(idx, gpio_dev)), \
|
||||
}; \
|
||||
\
|
||||
static struct i2c_it8xxx2_data i2c_it8xxx2_data_##idx; \
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ properties:
|
|||
required: true
|
||||
description: Ordinal identifying the port
|
||||
|
||||
gpio-dev:
|
||||
type: phandle
|
||||
required: true
|
||||
description: Get the handle of the GPIO device
|
||||
|
||||
pinctrl-0:
|
||||
type: phandle
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -603,6 +603,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_0";
|
||||
port-num = <0>;
|
||||
gpio-dev = <&gpiob>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk0>; /* GPB3 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data0>; /* GPB4 */
|
||||
};
|
||||
|
|
@ -616,6 +617,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_1";
|
||||
port-num = <1>;
|
||||
gpio-dev = <&gpioc>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk1>; /* GPC1 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data1>; /* GPC2 */
|
||||
};
|
||||
|
|
@ -629,6 +631,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_2";
|
||||
port-num = <2>;
|
||||
gpio-dev = <&gpiof>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk2>; /* GPF6 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data2>; /* GPF7 */
|
||||
};
|
||||
|
|
@ -642,6 +645,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_3";
|
||||
port-num = <3>;
|
||||
gpio-dev = <&gpioh>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk3>; /* GPH1 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data3>; /* GPH2 */
|
||||
};
|
||||
|
|
@ -655,6 +659,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_4";
|
||||
port-num = <4>;
|
||||
gpio-dev = <&gpioe>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk4>; /* GPE0 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data4>; /* GPE7 */
|
||||
};
|
||||
|
|
@ -668,6 +673,7 @@
|
|||
status = "disabled";
|
||||
label = "I2C_5";
|
||||
port-num = <5>;
|
||||
gpio-dev = <&gpioa>;
|
||||
pinctrl-0 = <&pinctrl_i2c_clk5>; /* GPA4 */
|
||||
pinctrl-1 = <&pinctrl_i2c_data5>; /* GPA5 */
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue