From df8dcac8a380147e02582c25016406cfe6bba56e Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Tue, 27 Mar 2018 17:52:26 +0300 Subject: [PATCH 1/5] dm: i2c: dts: Add gpios and pinctrl device tree properties The commit describe usage of gpios and pinctrl device tree properties in order to enable gpio-based software deblocking. Signed-off-by: Alexander Kochetkov --- doc/device-tree-bindings/i2c/i2c.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/device-tree-bindings/i2c/i2c.txt b/doc/device-tree-bindings/i2c/i2c.txt index ea918dd..de818d4 100644 --- a/doc/device-tree-bindings/i2c/i2c.txt +++ b/doc/device-tree-bindings/i2c/i2c.txt @@ -12,6 +12,11 @@ property which allows the chip offset length to be selected. Optional properties: - u-boot,i2c-offset-len - length of chip offset in bytes. If omitted the default value of 1 is used. +- gpios = , ; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c_xfer>; + pinctrl-1 = <&i2c_gpio>; + Pin description for I2C bus software deblocking. Example @@ -26,3 +31,11 @@ i2c4: i2c@12ca0000 { ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; }; }; + +&i2c1 { + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&i2c1_xfer>; + pinctrl-1 = <&i2c1_gpio>; + gpios = <&gpio1 26 GPIO_ACTIVE_LOW>, /* SDA */ + <&gpio1 27 GPIO_ACTIVE_LOW>; /* SCL */ +}; From aa54192d4a87460e105264a8156f1d7b1d212b0b Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Tue, 27 Mar 2018 17:52:27 +0300 Subject: [PATCH 2/5] dm: i2c: implement gpio-based I2C deblock The commit implement a gpio-based software deblocking. The code extract I2C pins description from device tree, switch pins to GPIO mode, toggle SCL until slave release SDA, send I2C stop and switch I2C pins back to I2C mode. Signed-off-by: Alexander Kochetkov --- drivers/i2c/i2c-uclass.c | 118 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c index 920811a..4ac6ef8 100644 --- a/drivers/i2c/i2c-uclass.c +++ b/drivers/i2c/i2c-uclass.c @@ -11,9 +11,19 @@ #include #include #include +#include +#ifdef CONFIG_DM_GPIO +#include +#endif #define I2C_MAX_OFFSET_LEN 4 +enum { + PIN_SDA = 0, + PIN_SCL, + PIN_COUNT, +}; + /* Useful debugging function */ void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs) { @@ -445,20 +455,110 @@ int i2c_get_chip_offset_len(struct udevice *dev) return chip->offset_len; } +#ifdef CONFIG_DM_GPIO +static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit) +{ + if (bit) + dm_gpio_set_dir_flags(pin, GPIOD_IS_IN); + else + dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT | + GPIOD_ACTIVE_LOW | + GPIOD_IS_OUT_ACTIVE); +} + +static int i2c_gpio_get_pin(struct gpio_desc *pin) +{ + return dm_gpio_get_value(pin); +} + +static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin, + struct gpio_desc *scl_pin) +{ + int counter = 9; + int ret = 0; + + i2c_gpio_set_pin(sda_pin, 1); + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + + /* Toggle SCL until slave release SDA */ + while (counter-- >= 0) { + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + i2c_gpio_set_pin(scl_pin, 0); + udelay(5); + if (i2c_gpio_get_pin(sda_pin)) + break; + } + + /* Then, send I2C stop */ + i2c_gpio_set_pin(sda_pin, 0); + udelay(5); + + i2c_gpio_set_pin(scl_pin, 1); + udelay(5); + + i2c_gpio_set_pin(sda_pin, 1); + udelay(5); + + if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin)) + ret = -EREMOTEIO; + + return ret; +} + +static int i2c_deblock_gpio(struct udevice *bus) +{ + struct gpio_desc gpios[PIN_COUNT]; + int ret, ret0; + + ret = gpio_request_list_by_name(bus, "gpios", gpios, + ARRAY_SIZE(gpios), GPIOD_IS_IN); + if (ret != ARRAY_SIZE(gpios)) { + debug("%s: I2C Node '%s' has no 'gpios' property %s\n", + __func__, dev_read_name(bus), bus->name); + if (ret >= 0) { + gpio_free_list(bus, gpios, ret); + ret = -ENOENT; + } + goto out; + } + + ret = pinctrl_select_state(bus, "gpio"); + if (ret) { + debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n", + __func__, dev_read_name(bus), bus->name); + goto out_no_pinctrl; + } + + ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]); + + ret = pinctrl_select_state(bus, "default"); + if (ret) { + debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n", + __func__, dev_read_name(bus), bus->name); + } + + ret = !ret ? ret0 : ret; + +out_no_pinctrl: + gpio_free_list(bus, gpios, ARRAY_SIZE(gpios)); +out: + return ret; +} +#else +static int i2c_deblock_gpio(struct udevice *bus) +{ + return -ENOSYS; +} +#endif // CONFIG_DM_GPIO + int i2c_deblock(struct udevice *bus) { struct dm_i2c_ops *ops = i2c_get_ops(bus); - /* - * We could implement a software deblocking here if we could get - * access to the GPIOs used by I2C, and switch them to GPIO mode - * and then back to I2C. This is somewhat beyond our powers in - * driver model at present, so for now just fail. - * - * See https://patchwork.ozlabs.org/patch/399040/ - */ if (!ops->deblock) - return -ENOSYS; + return i2c_deblock_gpio(bus); return ops->deblock(bus); } From 2df71d6d6a364f92510cfa657d7407cf7e516570 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Wed, 28 Mar 2018 14:37:42 +0200 Subject: [PATCH 3/5] i2c: ihs_i2c: Use new fpgamap interface The fpgamap interface has been switched to a "single function + data size" interface. Reflect this change in the IHS I2C driver. Signed-off-by: Mario Six --- drivers/i2c/ihs_i2c.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/ihs_i2c.c b/drivers/i2c/ihs_i2c.c index 9298521..82abb43 100644 --- a/drivers/i2c/ihs_i2c.c +++ b/drivers/i2c/ihs_i2c.c @@ -99,7 +99,8 @@ static int wait_for_int(bool read) #endif #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -110,7 +111,8 @@ static int wait_for_int(bool read) if (ctr++ > 5000) return 1; #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -128,6 +130,7 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, #endif { u16 val; + u16 data; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); struct udevice *fpga; @@ -136,13 +139,14 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, #endif /* Clear interrupt status */ + data = I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV; #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_INTERRUPT_STATUS, - I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV); - fpgamap_read16(fpga, priv->addr + REG_INTERRUPT_STATUS, &val); + fpgamap_write(fpga, priv->addr + REG_INTERRUPT_STATUS, &data, + FPGAMAP_SIZE_16); + fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, + FPGAMAP_SIZE_16); #else - I2C_SET_REG(interrupt_status, I2CINT_ERROR_EV - | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV); + I2C_SET_REG(interrupt_status, data); I2C_GET_REG(interrupt_status, &val); #endif @@ -153,26 +157,24 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, if (len > 1) val |= buffer[1] << 8; #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, val); + fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, &val, + FPGAMAP_SIZE_16); #else I2C_SET_REG(write_mailbox_ext, val); #endif } + data = I2CMB_NATIVE + | (read ? 0 : I2CMB_WRITE) + | (chip << 1) + | ((len > 1) ? I2CMB_2BYTE : 0) + | (is_last ? 0 : I2CMB_HOLD_BUS); + #ifdef CONFIG_DM_I2C - fpgamap_write16(fpga, priv->addr + REG_WRITE_MAILBOX, - I2CMB_NATIVE - | (read ? I2CMB_READ : I2CMB_WRITE) - | (chip << 1) - | ((len > 1) ? I2CMB_2BYTE : I2CMB_1BYTE) - | (!is_last ? I2CMB_HOLD_BUS : I2CMB_DONT_HOLD_BUS)); + fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX, &data, + FPGAMAP_SIZE_16); #else - I2C_SET_REG(write_mailbox, - I2CMB_NATIVE - | (read ? 0 : I2CMB_WRITE) - | (chip << 1) - | ((len > 1) ? I2CMB_2BYTE : 0) - | (is_last ? 0 : I2CMB_HOLD_BUS)); + I2C_SET_REG(write_mailbox, data); #endif #ifdef CONFIG_DM_I2C @@ -185,7 +187,8 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, /* If we want to read, get the bytes from the mailbox */ if (read) { #ifdef CONFIG_DM_I2C - fpgamap_read16(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val); + fpgamap_read(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val, + FPGAMAP_SIZE_16); #else I2C_GET_REG(read_mailbox_ext, &val); #endif From d934832de6e62ca21c99a4fbad808175801f4018 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Wed, 28 Mar 2018 14:37:43 +0200 Subject: [PATCH 4/5] i2c: fsl: Use dev_read_addr Since bus translations are now fully supported, use a plain "dev_read_addr" to get the device address from the device tree. Signed-off-by: Mario Six --- drivers/i2c/fsl_i2c.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index cb0f5ea..ad8eea4 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -573,11 +573,8 @@ static int fsl_i2c_set_bus_speed(struct udevice *bus, uint speed) static int fsl_i2c_ofdata_to_platdata(struct udevice *bus) { struct fsl_i2c_dev *dev = dev_get_priv(bus); - fdt_addr_t addr; - addr = dev_read_u32_default(bus, "reg", -1); - - dev->base = map_sysmem(CONFIG_SYS_IMMR + addr, sizeof(struct fsl_i2c_base)); + dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct fsl_i2c_base)); if (!dev->base) return -ENOMEM; From e5c762f5a7dbeaa21870720e8daf656153e1aef9 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Wed, 28 Mar 2018 14:37:44 +0200 Subject: [PATCH 5/5] i2c: fsl: Add option to get clock from DT Add an option to get the clock speed from the device tree, hence adding compatibility with DM clock drivers. Signed-off-by: Mario Six --- drivers/i2c/fsl_i2c.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index ad8eea4..450a91d 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -12,6 +12,7 @@ #include /* Functional interface */ #include #include /* HW definitions */ +#include #include #include @@ -573,6 +574,7 @@ static int fsl_i2c_set_bus_speed(struct udevice *bus, uint speed) static int fsl_i2c_ofdata_to_platdata(struct udevice *bus) { struct fsl_i2c_dev *dev = dev_get_priv(bus); + struct clk clock; dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct fsl_i2c_base)); @@ -584,7 +586,11 @@ static int fsl_i2c_ofdata_to_platdata(struct udevice *bus) 0x7f); dev->speed = dev_read_u32_default(bus, "clock-frequency", 400000); - dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : gd->arch.i2c1_clk; + if (!clk_get_by_index(bus, 0, &clock)) + dev->i2c_clk = clk_get_rate(&clock); + else + dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : + gd->arch.i2c1_clk; return 0; }