X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fi2c%2Fmxc_i2c.c;h=eb789f5bff492246bf19ae5698b6b4367607d818;hb=10fa3ee09b4219e96128bf56b380a47a2b905134;hp=b2d15c9b6a82e201d48ef501657919aae9643047;hpb=67ecb84ccbfd609170978833fd09b0b87fc4b630;p=u-boot diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index b2d15c9b6a..eb789f5bff 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -17,12 +17,13 @@ #include #include #include -#include +#include #include #include #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -31,6 +32,14 @@ DECLARE_GLOBAL_DATA_PTR; #define IMX_I2C_REGSHIFT 2 #define VF610_I2C_REGSHIFT 0 + +#define I2C_EARLY_INIT_INDEX 0 +#ifdef CONFIG_SYS_I2C_IFDR_DIV +#define I2C_IFDR_DIV_CONSERVATIVE CONFIG_SYS_I2C_IFDR_DIV +#else +#define I2C_IFDR_DIV_CONSERVATIVE 0x7e +#endif + /* Register index */ #define IADR 0 #define IFDR 1 @@ -334,17 +343,74 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) } #else /* - * Since pinmux is not supported, implement a weak function here. - * You can implement your i2c_bus_idle in board file. When pinctrl - * is supported, this can be removed. + * See Linux Documentation/devicetree/bindings/i2c/i2c-imx.txt + * " + * scl-gpios: specify the gpio related to SCL pin + * sda-gpios: specify the gpio related to SDA pin + * add pinctrl to configure i2c pins to gpio function for i2c + * bus recovery, call it "gpio" state + * " + * + * The i2c_idle_bus is an implementation following Linux Kernel. */ -int __i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) +int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) { - return 0; -} + struct udevice *bus = i2c_bus->bus; + struct gpio_desc *scl_gpio = &i2c_bus->scl_gpio; + struct gpio_desc *sda_gpio = &i2c_bus->sda_gpio; + int sda, scl; + int i, ret = 0; + ulong elapsed, start_time; -int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) - __attribute__((weak, alias("__i2c_idle_bus"))); + if (pinctrl_select_state(bus, "gpio")) { + dev_dbg(bus, "Can not to switch to use gpio pinmux\n"); + /* + * GPIO pinctrl for i2c force idle is not a must, + * but it is strongly recommended to be used. + * Because it can help you to recover from bad + * i2c bus state. Do not return failure, because + * it is not a must. + */ + return 0; + } + + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN); + scl = dm_gpio_get_value(scl_gpio); + sda = dm_gpio_get_value(sda_gpio); + + if ((sda & scl) == 1) + goto exit; /* Bus is idle already */ + + /* Send high and low on the SCL line */ + for (i = 0; i < 9; i++) { + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_OUT); + dm_gpio_set_value(scl_gpio, 0); + udelay(50); + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + udelay(50); + } + start_time = get_timer(0); + for (;;) { + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN); + scl = dm_gpio_get_value(scl_gpio); + sda = dm_gpio_get_value(sda_gpio); + if ((sda & scl) == 1) + break; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ + ret = -EBUSY; + printf("%s: failed to clear bus, sda=%d scl=%d\n", __func__, sda, scl); + break; + } + } + +exit: + pinctrl_select_state(bus, "default"); + return ret; +} #endif static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip, @@ -601,6 +667,25 @@ void bus_i2c_init(int index, int speed, int unused, bus_i2c_set_bus_speed(&mxc_i2c_buses[index], speed); } +/* + * Early init I2C for prepare read the clk through I2C. + */ +void i2c_early_init_f(void) +{ + ulong base = mxc_i2c_buses[I2C_EARLY_INIT_INDEX].base; + bool quirk = mxc_i2c_buses[I2C_EARLY_INIT_INDEX].driver_data + & I2C_QUIRK_FLAG ? true : false; + int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT; + + /* Set I2C divider value */ + writeb(I2C_IFDR_DIV_CONSERVATIVE, base + (IFDR << reg_shift)); + /* Reset module */ + writeb(I2CR_IDIS, base + (I2CR << reg_shift)); + writeb(0, base + (I2SR << reg_shift)); + /* Enable I2C */ + writeb(I2CR_IEN, base + (I2CR << reg_shift)); +} + /* * Init I2C Bus */ @@ -664,8 +749,10 @@ static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) static int mxc_i2c_probe(struct udevice *bus) { struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus); + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(bus); fdt_addr_t addr; - int ret; + int ret, ret2; i2c_bus->driver_data = dev_get_driver_data(bus); @@ -675,12 +762,35 @@ static int mxc_i2c_probe(struct udevice *bus) i2c_bus->base = addr; i2c_bus->index = bus->seq; + i2c_bus->bus = bus; /* Enable clk */ ret = enable_i2c_clk(1, bus->seq); if (ret < 0) return ret; + /* + * See Documentation/devicetree/bindings/i2c/i2c-imx.txt + * Use gpio to force bus idle when necessary. + */ + ret = fdt_stringlist_search(fdt, node, "pinctrl-names", "gpio"); + if (ret < 0) { + debug("i2c bus %d at 0x%2lx, no gpio pinctrl state.\n", bus->seq, i2c_bus->base); + } else { + ret = gpio_request_by_name_nodev(fdt, node, "scl-gpios", + 0, &i2c_bus->scl_gpio, + GPIOD_IS_OUT); + ret2 = gpio_request_by_name_nodev(fdt, node, "sda-gpios", + 0, &i2c_bus->sda_gpio, + GPIOD_IS_OUT); + if (!dm_gpio_is_valid(&i2c_bus->sda_gpio) | + !dm_gpio_is_valid(&i2c_bus->scl_gpio) | + ret | ret2) { + dev_err(dev, "i2c bus %d at %lu, fail to request scl/sda gpio\n", bus->seq, i2c_bus->base); + return -ENODEV; + } + } + ret = i2c_idle_bus(i2c_bus); if (ret < 0) { /* Disable clk */