[U-Boot] [PATCH 0/3] dm: i2c: enable driver model for software i2c

This patchset enables driver model support for software i2c bus driver. It was tested on Trats2 and Odroid U3 devices.
It can be tested on any other device by just modifying the dts file, first by disabling the hardware i2c bus and then, as it is described in the Kconfig help entry, setup soft-i2c node.
The drivers, which are using the old api are not converted with this patchset. I hope that maintainers will do this if required.
Probably the software i2c is used for PMIC devices not only for Trats2, or Universal C210, so I suggest to wait with moving the drivers until the pmic is done - this will prevent adding temporary code.
Przemyslaw Marczak (3): dm: i2c soft: enable driver model for software i2c driver Kconfig: i2c: remove wrong help message related to dm i2c Kconfig: i2c: add entry for driver-model software i2c
drivers/i2c/Kconfig | 54 +++++-- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 444 insertions(+), 21 deletions(-)

This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes: - alias: to keep proper bus order - compatible: 'soft-i2c' - clock-frequency [Hz] - clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
/* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] }; };
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de --- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /* + * (C) Copyright 2015, Samsung Electronics + * Przemyslaw Marczak p.marczak@samsung.com + * * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs@denx.de. * Changes for multibus/multiadapter I2C support. @@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h> - # ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif @@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif + +# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \ + do { } while (0) +# endif + +# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \ + do { } while (0) +# endif + +# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \ + do { } while (0) +# endif + +# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif + +# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \ + do { \ + if (bit) { \ + sda->flags &= ~GPIOD_IS_OUT; \ + sda->flags |= GPIOD_IS_IN; \ + dm_gpio_set_dir(sda); \ + } else { \ + sda->flags &= ~GPIOD_IS_IN; \ + sda->flags |= GPIOD_IS_OUT; \ + dm_gpio_set_dir(sda); \ + dm_gpio_set_value(sda, 0); \ + } \ + I2C_GPIO_SYNC(sda); \ + } while (0) +# endif + +# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \ + do { \ + scl->flags &= ~GPIOD_IS_IN; \ + scl->flags |= GPIOD_IS_OUT; \ + dm_gpio_set_dir(scl); \ + dm_gpio_set_value(scl, bit); \ + I2C_GPIO_SYNC(scl); \ + } while (0) +# endif + +# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif + +#endif /* CONFIG_DM_I2C_SOFT */ + /*----------------------------------------------------------------------- * Definitions */ @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
- #ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \ @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR; /*----------------------------------------------------------------------- * Local functions */ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int); - #if !defined(CONFIG_SYS_I2C_INIT_BOARD) /*----------------------------------------------------------------------- * Send a reset sequence consisting of 9 clocks with the data signal high * to clock any confused device back into an idle state. Also send a * <stop> at the end of the sequence for belts & suspenders. */ +#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + + I2C_SCL(scl, 1); + I2C_SDA(sda, 1); +#ifdef I2C_INIT + I2C_INIT(scl, sda); +#endif + I2C_TRISTATE(sda); + for (j = 0; j < 9; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + } + send_stop(scl, sda, delay); + I2C_TRISTATE(sda); +} +#endif /* CONFIG_SYS_I2C_SOFT */ #endif
/*----------------------------------------------------------------------- * START: High -> Low on SDA while SCL is High */ +#ifdef CONFIG_SYS_I2C_SOFT static void send_start(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -184,10 +283,25 @@ static void send_start(void) I2C_SDA(0); I2C_DELAY; } +#else +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */
+ I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_ACTIVE(sda); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_SDA(sda, 0); + I2C_DELAY(delay); +} +#endif /* CONFIG_SYS_I2C_SOFT */ /*----------------------------------------------------------------------- * STOP: Low -> High on SDA while SCL is High */ +#ifdef CONFIG_SYS_I2C_SOFT static void send_stop(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -203,10 +317,28 @@ static void send_stop(void) I2C_DELAY; I2C_TRISTATE; } +#else +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, 0); + I2C_ACTIVE(sda); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_DELAY(delay); + I2C_TRISTATE(sda); +} +#endif /* CONFIG_SYS_I2C_SOFT */
/*----------------------------------------------------------------------- * ack should be I2C_ACK or I2C_NOACK */ +#ifdef CONFIG_SYS_I2C_SOFT static void send_ack(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -222,10 +354,29 @@ static void send_ack(int ack) I2C_SCL(0); I2C_DELAY; } +#else +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_ACTIVE(sda); + I2C_SDA(sda, ack); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + I2C_SCL(scl, 0); + I2C_DELAY(delay); +} +#endif
/*----------------------------------------------------------------------- * Send 8 bits and look for an acknowledgement. */ +#ifdef CONFIG_SYS_I2C_SOFT static int write_byte(uchar data) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -263,11 +414,52 @@ static int write_byte(uchar data)
return(nack); /* not a nack is an ack */ } +#else +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, uchar data) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + int nack; + + I2C_ACTIVE(sda); + for (j = 0; j < 8; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, data & 0x80); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + + data <<= 1; + } + + /* + * Look for an <ACK>(negative logic) and return it. + */ + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_TRISTATE(sda); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + nack = I2C_READ(sda); + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_ACTIVE(sda); + + return nack; /* not a nack is an ack */ +} +#endif
/*----------------------------------------------------------------------- * if ack == I2C_ACK, ACK the byte so can continue reading, else * send I2C_NOACK to end the read. */ +#ifdef CONFIG_SYS_I2C_SOFT static uchar read_byte(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
return(data); } +#else +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int data; + int j; + + /* + * Read 8 bits, MSB first. + */ + I2C_TRISTATE(sda); + I2C_SDA(sda, 1); + data = 0; + for (j = 0; j < 8; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + data <<= 1; + data |= I2C_READ(sda); + I2C_DELAY(delay); + } + send_ack(scl, sda, delay, ack);
+ return data; +} +#endif /* CONFIG_SYS_I2C_SOFT */ /*----------------------------------------------------------------------- * Initialization */ +#ifdef CONFIG_SYS_I2C_SOFT static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) send_reset (); #endif } +#endif
/*----------------------------------------------------------------------- * Probe to see if a chip is present. Also good for checking for the * completion of EEPROM writes since the chip stops responding until * the write completes (typically 10mSec). */ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
return (rc ? 1 : 0); } +#endif
/*----------------------------------------------------------------------- * Read bytes */ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -409,10 +633,12 @@ static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(0); } +#endif
/*----------------------------------------------------------------------- * Write bytes */ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -444,10 +670,12 @@ static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(failures); } +#endif
/* * Register soft i2c adapters */ +#ifdef CONFIG_SYS_I2C_SOFT U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, soft_i2c_read, soft_i2c_write, NULL, CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, CONFIG_SYS_I2C_SOFT_SLAVE_4, 3) #endif +#endif /* CONFIG_SYS_I2C_SOFT */ + +#ifdef CONFIG_DM_I2C_SOFT +struct soft_i2c_bus { + unsigned int speed; + unsigned long delay; + struct gpio_desc scl; + struct gpio_desc sda; +}; + +static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip, + uchar *buffer, int len, bool end_with_repeated_start) +{ + struct gpio_desc *scl = &i2c_bus->scl; + struct gpio_desc *sda = &i2c_bus->sda; + unsigned int delay = i2c_bus->delay; + int failures = 0; + + PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len); + + send_start(scl, sda, delay); + if (write_byte(scl, sda, delay, chip << 1)) { + send_stop(scl, sda, delay); + PRINTD("i2c_write, no chip responded %02X\n", chip); + return -ENODEV; + } + + while (len-- > 0) { + if (write_byte(scl, sda, delay, *buffer++)) + failures++; + } + + send_stop(scl, sda, delay); + + return failures; +} + +static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip, + uchar *buffer, int len) +{ + struct gpio_desc *scl = &i2c_bus->scl; + struct gpio_desc *sda = &i2c_bus->sda; + unsigned int delay = i2c_bus->delay; + + PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len); + + send_start(scl, sda, delay); + write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */ + + while (len-- > 0) + *buffer++ = read_byte(scl, sda, delay, len == 0); + + send_stop(scl, sda, delay); + + return 0; +} + +static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct soft_i2c_bus *i2c_bus = dev_get_priv(dev); + int ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + if (msg->flags & I2C_M_RD) { + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, + msg->len); + } else { + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, + msg->len, next_is_read); + } + if (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{ + struct soft_i2c_bus *i2c_bus = dev_get_priv(dev); + struct gpio_desc *scl = &i2c_bus->scl; + struct gpio_desc *sda = &i2c_bus->sda; + unsigned int delay = i2c_bus->delay; + int ret; + + send_start(scl, sda, delay); + ret = write_byte(scl, sda, delay, (chip << 1) | 0); + send_stop(scl, sda, delay); + + PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n", + __func__, dev->seq, dev->name, chip, chip_flags, ret); + + return ret; +} + +static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + struct soft_i2c_bus *i2c_bus = dev_get_priv(dev); + struct gpio_desc *scl = &i2c_bus->scl; + struct gpio_desc *sda = &i2c_bus->sda; + + i2c_bus->speed = speed; + i2c_bus->delay = lldiv(1000000, speed << 2); + + send_reset(scl, sda, i2c_bus->delay); + + PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n", + __func__, dev->seq, dev->name, speed, i2c_bus->delay); + + return 0; +} + +static int soft_i2c_ofdata_to_platdata(struct udevice *dev) +{ + struct soft_i2c_bus *i2c_bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + char *pin_name; + int ret; + + pin_name = "clock-pin"; + ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name, + 0, &i2c_bus->scl, GPIOD_IS_OUT); + if (ret) + goto error; + + pin_name = "data-pin"; + ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name, + 0, &i2c_bus->sda, GPIOD_IS_OUT); + if (ret) + goto error; + + PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name); + + return 0; +error: + error("Can't get %s for soft i2c dev: %s", pin_name, dev->name); + return ret; +} + +static const struct dm_i2c_ops soft_i2c_ops = { + .xfer = soft_i2c_xfer, + .probe_chip = soft_i2c_probe, + .set_bus_speed = soft_i2c_set_bus_speed, +}; + +static const struct udevice_id soft_i2c_ids[] = { + { .compatible = "soft-i2c" }, + { } +}; + +U_BOOT_DRIVER(soft_i2c) = { + .name = "soft-i2c", + .id = UCLASS_I2C, + .of_match = soft_i2c_ids, + .ofdata_to_platdata = soft_i2c_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct soft_i2c_bus), + .ops = &soft_i2c_ops, +}; +#endif /* CONFIG_DM_I2C_SOFT */

Hi Przemyslaw,
This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
/* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] }; };
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
@@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h>
# ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif @@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif
+# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \
- do { } while (0)
+# endif
+# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \
- do { } while (0)
+# endif
+# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \
- do { } while (0)
+# endif
+# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif
+# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \
- do { \
if (bit) { \
sda->flags &= ~GPIOD_IS_OUT; \
sda->flags |= GPIOD_IS_IN; \
dm_gpio_set_dir(sda); \
} else { \
sda->flags &= ~GPIOD_IS_IN; \
sda->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(sda); \
dm_gpio_set_value(sda, 0); \
} \
I2C_GPIO_SYNC(sda); \
- } while (0)
+# endif
+# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \
- do { \
scl->flags &= ~GPIOD_IS_IN; \
scl->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(scl); \
dm_gpio_set_value(scl, bit); \
I2C_GPIO_SYNC(scl); \
- } while (0)
+# endif
+# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif
+#endif /* CONFIG_DM_I2C_SOFT */
/*-----------------------------------------------------------------------
- Definitions
*/ @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
#ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \ @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR; /*-----------------------------------------------------------------------
- Local functions
*/ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
#if !defined(CONFIG_SYS_I2C_INIT_BOARD) /*-----------------------------------------------------------------------
- Send a reset sequence consisting of 9 clocks with the data signal
high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- I2C_SCL(scl, 1);
- I2C_SDA(sda, 1);
+#ifdef I2C_INIT
- I2C_INIT(scl, sda);
+#endif
- I2C_TRISTATE(sda);
- for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
- }
- send_stop(scl, sda, delay);
- I2C_TRISTATE(sda);
+} +#endif /* CONFIG_SYS_I2C_SOFT */ #endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_start(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -184,10 +283,25 @@ static void send_start(void) I2C_SDA(0); I2C_DELAY; } +#else +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- STOP: Low -> High on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_stop(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -203,10 +317,28 @@ static void send_stop(void) I2C_DELAY; I2C_TRISTATE; } +#else +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- I2C_SCL(scl, 0);
- I2C_DELAY(delay);
- I2C_SDA(sda, 0);
- I2C_ACTIVE(sda);
- I2C_DELAY(delay);
- I2C_SCL(scl, 1);
- I2C_DELAY(delay);
- I2C_SDA(sda, 1);
- I2C_DELAY(delay);
- I2C_TRISTATE(sda);
+} +#endif /* CONFIG_SYS_I2C_SOFT */
/*-----------------------------------------------------------------------
- ack should be I2C_ACK or I2C_NOACK
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_ack(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -222,10 +354,29 @@ static void send_ack(int ack) I2C_SCL(0); I2C_DELAY; } +#else +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- I2C_SCL(scl, 0);
- I2C_DELAY(delay);
- I2C_ACTIVE(sda);
- I2C_SDA(sda, ack);
- I2C_DELAY(delay);
- I2C_SCL(scl, 1);
- I2C_DELAY(delay);
- I2C_DELAY(delay);
- I2C_SCL(scl, 0);
- I2C_DELAY(delay);
+} +#endif
/*-----------------------------------------------------------------------
- Send 8 bits and look for an acknowledgement.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int write_byte(uchar data) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -263,11 +414,52 @@ static int write_byte(uchar data)
return(nack); /* not a nack is an ack */ } +#else +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- int nack;
- I2C_ACTIVE(sda);
- for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
data <<= 1;
- }
- /*
* Look for an <ACK>(negative logic) and return it.
*/
- I2C_SCL(scl, 0);
- I2C_DELAY(delay);
- I2C_SDA(sda, 1);
- I2C_TRISTATE(sda);
- I2C_DELAY(delay);
- I2C_SCL(scl, 1);
- I2C_DELAY(delay);
- I2C_DELAY(delay);
- nack = I2C_READ(sda);
- I2C_SCL(scl, 0);
- I2C_DELAY(delay);
- I2C_ACTIVE(sda);
- return nack; /* not a nack is an ack */
+} +#endif
/*-----------------------------------------------------------------------
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static uchar read_byte(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
return(data); } +#else +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int data;
int j;
/*
* Read 8 bits, MSB first.
*/
I2C_TRISTATE(sda);
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- Initialization
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) send_reset (); #endif } +#endif
/*-----------------------------------------------------------------------
- Probe to see if a chip is present. Also good for checking for the
- completion of EEPROM writes since the chip stops responding until
- the write completes (typically 10mSec).
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) return (rc ? 1 : 0); } +#endif
/*-----------------------------------------------------------------------
- Read bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -409,10 +633,12 @@ static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(0); } +#endif
/*-----------------------------------------------------------------------
- Write bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -444,10 +670,12 @@ static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(failures); } +#endif
/*
- Register soft i2c adapters
*/ +#ifdef CONFIG_SYS_I2C_SOFT U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, soft_i2c_read, soft_i2c_write, NULL, CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, CONFIG_SYS_I2C_SOFT_SLAVE_4, 3) #endif +#endif /* CONFIG_SYS_I2C_SOFT */
+#ifdef CONFIG_DM_I2C_SOFT +struct soft_i2c_bus {
- unsigned int speed;
- unsigned long delay;
- struct gpio_desc scl;
- struct gpio_desc sda;
+};
+static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len, bool
end_with_repeated_start) +{
- struct gpio_desc *scl = &i2c_bus->scl;
- struct gpio_desc *sda = &i2c_bus->sda;
- unsigned int delay = i2c_bus->delay;
- int failures = 0;
- PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip,
buffer, len); +
- send_start(scl, sda, delay);
- if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -ENODEV;
- }
- while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
- }
- send_stop(scl, sda, delay);
- return failures;
+}
+static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len)
+{
- struct gpio_desc *scl = &i2c_bus->scl;
- struct gpio_desc *sda = &i2c_bus->sda;
- unsigned int delay = i2c_bus->delay;
- PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip,
buffer, len); +
- send_start(scl, sda, delay);
- write_byte(scl, sda, delay, (chip << 1) | 1); /* read
cycle */ +
- while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
- send_stop(scl, sda, delay);
- return 0;
+}
+static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
- struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
- int ret;
- for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags &
I2C_M_RD);
if (msg->flags & I2C_M_RD) {
ret = i2c_read_data(i2c_bus, msg->addr,
msg->buf,
msg->len);
} else {
ret = i2c_write_data(i2c_bus, msg->addr,
msg->buf,
msg->len, next_is_read);
}
if (ret)
return -EREMOTEIO;
- }
- return 0;
+}
+static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{
- struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
- struct gpio_desc *scl = &i2c_bus->scl;
- struct gpio_desc *sda = &i2c_bus->sda;
- unsigned int delay = i2c_bus->delay;
- int ret;
- send_start(scl, sda, delay);
- ret = write_byte(scl, sda, delay, (chip << 1) | 0);
- send_stop(scl, sda, delay);
- PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
- return ret;
+}
+static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{
- struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
- struct gpio_desc *scl = &i2c_bus->scl;
- struct gpio_desc *sda = &i2c_bus->sda;
- i2c_bus->speed = speed;
- i2c_bus->delay = lldiv(1000000, speed << 2);
- send_reset(scl, sda, i2c_bus->delay);
- PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu
us)\n",
__func__, dev->seq, dev->name, speed, i2c_bus->delay);
- return 0;
+}
+static int soft_i2c_ofdata_to_platdata(struct udevice *dev) +{
- struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
- const void *blob = gd->fdt_blob;
- char *pin_name;
- int ret;
- pin_name = "clock-pin";
- ret = gpio_request_by_name_nodev(blob, dev->of_offset,
pin_name,
0, &i2c_bus->scl,
GPIOD_IS_OUT);
- if (ret)
goto error;
- pin_name = "data-pin";
- ret = gpio_request_by_name_nodev(blob, dev->of_offset,
pin_name,
0, &i2c_bus->sda,
GPIOD_IS_OUT);
- if (ret)
goto error;
- PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq,
dev->name); +
- return 0;
+error:
- error("Can't get %s for soft i2c dev: %s", pin_name,
dev->name);
- return ret;
+}
+static const struct dm_i2c_ops soft_i2c_ops = {
- .xfer = soft_i2c_xfer,
- .probe_chip = soft_i2c_probe,
- .set_bus_speed = soft_i2c_set_bus_speed,
+};
+static const struct udevice_id soft_i2c_ids[] = {
- { .compatible = "soft-i2c" },
- { }
+};
+U_BOOT_DRIVER(soft_i2c) = {
- .name = "soft-i2c",
- .id = UCLASS_I2C,
- .of_match = soft_i2c_ids,
- .ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
- .priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
- .ops = &soft_i2c_ops,
+}; +#endif /* CONFIG_DM_I2C_SOFT */
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

Hi Przemyslaw,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>; /* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] };
};
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Is the intention to retire the old (non-driver-model) code? What boards use it?
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
@@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h>
# ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif @@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif
+# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \
do { } while (0)
+# endif
+# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif
+# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \
do { \
if (bit) { \
sda->flags &= ~GPIOD_IS_OUT; \
sda->flags |= GPIOD_IS_IN; \
dm_gpio_set_dir(sda); \
} else { \
sda->flags &= ~GPIOD_IS_IN; \
sda->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(sda); \
dm_gpio_set_value(sda, 0); \
} \
I2C_GPIO_SYNC(sda); \
} while (0)
+# endif
+# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \
do { \
scl->flags &= ~GPIOD_IS_IN; \
scl->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(scl); \
dm_gpio_set_value(scl, bit); \
I2C_GPIO_SYNC(scl); \
} while (0)
+# endif
+# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif
+#endif /* CONFIG_DM_I2C_SOFT */
/*-----------------------------------------------------------------------
- Definitions
*/ @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
#ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \ @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR; /*-----------------------------------------------------------------------
- Local functions
*/ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
#if !defined(CONFIG_SYS_I2C_INIT_BOARD) /*-----------------------------------------------------------------------
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
+#ifdef I2C_INIT
I2C_INIT(scl, sda);
+#endif
I2C_TRISTATE(sda);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
}
send_stop(scl, sda, delay);
I2C_TRISTATE(sda);
For the new code I would much prefer that these become functions rather than macros. What is the benefit of using a macro?
+} +#endif /* CONFIG_SYS_I2C_SOFT */ #endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_start(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -184,10 +283,25 @@ static void send_start(void) I2C_SDA(0); I2C_DELAY; } +#else +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- STOP: Low -> High on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_stop(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -203,10 +317,28 @@ static void send_stop(void) I2C_DELAY; I2C_TRISTATE; } +#else +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_TRISTATE(sda);
+} +#endif /* CONFIG_SYS_I2C_SOFT */
/*-----------------------------------------------------------------------
- ack should be I2C_ACK or I2C_NOACK
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_ack(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -222,10 +354,29 @@ static void send_ack(int ack) I2C_SCL(0); I2C_DELAY; } +#else +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
I2C_SDA(sda, ack);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
+} +#endif
/*-----------------------------------------------------------------------
- Send 8 bits and look for an acknowledgement.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int write_byte(uchar data) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -263,11 +414,52 @@ static int write_byte(uchar data)
return(nack); /* not a nack is an ack */
} +#else +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
int nack;
I2C_ACTIVE(sda);
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
data <<= 1;
}
/*
* Look for an <ACK>(negative logic) and return it.
*/
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_TRISTATE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
nack = I2C_READ(sda);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
return nack; /* not a nack is an ack */
+} +#endif
/*-----------------------------------------------------------------------
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static uchar read_byte(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
return(data);
} +#else +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int data;
int j;
/*
* Read 8 bits, MSB first.
*/
I2C_TRISTATE(sda);
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- Initialization
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) send_reset (); #endif } +#endif
/*-----------------------------------------------------------------------
- Probe to see if a chip is present. Also good for checking for the
- completion of EEPROM writes since the chip stops responding until
- the write completes (typically 10mSec).
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
return (rc ? 1 : 0);
} +#endif
/*-----------------------------------------------------------------------
- Read bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -409,10 +633,12 @@ static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(0); } +#endif
/*-----------------------------------------------------------------------
- Write bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -444,10 +670,12 @@ static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(failures); } +#endif
/*
- Register soft i2c adapters
*/ +#ifdef CONFIG_SYS_I2C_SOFT U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, soft_i2c_read, soft_i2c_write, NULL, CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, CONFIG_SYS_I2C_SOFT_SLAVE_4, 3) #endif +#endif /* CONFIG_SYS_I2C_SOFT */
+#ifdef CONFIG_DM_I2C_SOFT +struct soft_i2c_bus {
unsigned int speed;
unsigned long delay;
struct gpio_desc scl;
struct gpio_desc sda;
+};
+static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int failures = 0;
PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -ENODEV;
-EIO I think
}
while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
}
send_stop(scl, sda, delay);
return failures;
+}
+static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */
while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
send_stop(scl, sda, delay);
return 0;
+}
+static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
int ret;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD) {
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
msg->len);
} else {
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
msg->len, next_is_read);
}
if (ret)
return -EREMOTEIO;
}
return 0;
+}
+static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int ret;
send_start(scl, sda, delay);
ret = write_byte(scl, sda, delay, (chip << 1) | 0);
send_stop(scl, sda, delay);
PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
return ret;
+}
+static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
i2c_bus->speed = speed;
i2c_bus->delay = lldiv(1000000, speed << 2);
(scl, sda, i2c_bus->delay);
PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
__func__, dev->seq, dev->name, speed, i2c_bus->delay);
return 0;
+}
+static int soft_i2c_ofdata_to_platdata(struct udevice *dev) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
char *pin_name;
int ret;
pin_name = "clock-pin";
nit: You don't need this variable
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->scl, GPIOD_IS_OUT);
You should not use the nodev version when you have a device. Also below.
if (ret)
goto error;
pin_name = "data-pin";
nit: or here
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->sda, GPIOD_IS_OUT);
if (ret)
goto error;
PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
return 0;
+error:
error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
return ret;
+}
+static const struct dm_i2c_ops soft_i2c_ops = {
.xfer = soft_i2c_xfer,
.probe_chip = soft_i2c_probe,
.set_bus_speed = soft_i2c_set_bus_speed,
+};
+static const struct udevice_id soft_i2c_ids[] = {
{ .compatible = "soft-i2c" },
{ }
+};
+U_BOOT_DRIVER(soft_i2c) = {
.name = "soft-i2c",
.id = UCLASS_I2C,
.of_match = soft_i2c_ids,
.ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
.ops = &soft_i2c_ops,
+};
+#endif /* CONFIG_DM_I2C_SOFT */
1.9.1
Regards, Simon

Hello Simon, Przemyslaw,
Am 24.03.2015 00:38, schrieb Simon Glass:
Hi Przemyslaw,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>; /* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] };
};
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Is the intention to retire the old (non-driver-model) code? What boards use it?
I think not ... Hmm ... maybe you move the new code into a new file?
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Hups... seems I missed this patches ...
drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
Thanks for this work!
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
@@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h>
- # ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif
@@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif
+# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \
do { } while (0)
+# endif
+# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif
+# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \
do { \
if (bit) { \
sda->flags &= ~GPIOD_IS_OUT; \
sda->flags |= GPIOD_IS_IN; \
dm_gpio_set_dir(sda); \
} else { \
sda->flags &= ~GPIOD_IS_IN; \
sda->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(sda); \
dm_gpio_set_value(sda, 0); \
} \
I2C_GPIO_SYNC(sda); \
} while (0)
+# endif
+# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \
do { \
scl->flags &= ~GPIOD_IS_IN; \
scl->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(scl); \
dm_gpio_set_value(scl, bit); \
I2C_GPIO_SYNC(scl); \
} while (0)
+# endif
+# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif
+#endif /* CONFIG_DM_I2C_SOFT */
- /*-----------------------------------------------------------------------
*/
- Definitions
@@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
- #ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \
@@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR; /*-----------------------------------------------------------------------
- Local functions
*/ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
- #if !defined(CONFIG_SYS_I2C_INIT_BOARD) /*-----------------------------------------------------------------------
*/
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
+#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
+#ifdef I2C_INIT
I2C_INIT(scl, sda);
+#endif
I2C_TRISTATE(sda);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
}
send_stop(scl, sda, delay);
I2C_TRISTATE(sda);
For the new code I would much prefer that these become functions rather than macros. What is the benefit of using a macro?
I do not know the real reason, but I think it comes from powerpc times, where we had not yet such powerful SoCs and it saved some instructions ... I tend to say, lets move the dm part into a new file ... and let it us do like linux ...
@Simon: Do you pick up this patches through DM tree, or should I pick them up?
bye, Heiko
+} +#endif /* CONFIG_SYS_I2C_SOFT */ #endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_start(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -184,10 +283,25 @@ static void send_start(void) I2C_SDA(0); I2C_DELAY; } +#else +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- STOP: Low -> High on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_stop(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -203,10 +317,28 @@ static void send_stop(void) I2C_DELAY; I2C_TRISTATE; } +#else +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_TRISTATE(sda);
+} +#endif /* CONFIG_SYS_I2C_SOFT */
/*-----------------------------------------------------------------------
- ack should be I2C_ACK or I2C_NOACK
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_ack(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -222,10 +354,29 @@ static void send_ack(int ack) I2C_SCL(0); I2C_DELAY; } +#else +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
I2C_SDA(sda, ack);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
+} +#endif
/*-----------------------------------------------------------------------
- Send 8 bits and look for an acknowledgement.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int write_byte(uchar data) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -263,11 +414,52 @@ static int write_byte(uchar data)
return(nack); /* not a nack is an ack */
} +#else +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
int nack;
I2C_ACTIVE(sda);
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
data <<= 1;
}
/*
* Look for an <ACK>(negative logic) and return it.
*/
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_TRISTATE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
nack = I2C_READ(sda);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
return nack; /* not a nack is an ack */
+} +#endif
/*-----------------------------------------------------------------------
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static uchar read_byte(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
return(data);
} +#else +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int data;
int j;
/*
* Read 8 bits, MSB first.
*/
I2C_TRISTATE(sda);
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- Initialization
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) send_reset (); #endif } +#endif
/*-----------------------------------------------------------------------
- Probe to see if a chip is present. Also good for checking for the
- completion of EEPROM writes since the chip stops responding until
- the write completes (typically 10mSec).
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
return (rc ? 1 : 0);
} +#endif
/*-----------------------------------------------------------------------
- Read bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -409,10 +633,12 @@ static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(0); } +#endif
/*-----------------------------------------------------------------------
- Write bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -444,10 +670,12 @@ static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(failures); } +#endif
/*
- Register soft i2c adapters
*/ +#ifdef CONFIG_SYS_I2C_SOFT U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, soft_i2c_read, soft_i2c_write, NULL, CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, CONFIG_SYS_I2C_SOFT_SLAVE_4, 3) #endif +#endif /* CONFIG_SYS_I2C_SOFT */
+#ifdef CONFIG_DM_I2C_SOFT +struct soft_i2c_bus {
unsigned int speed;
unsigned long delay;
struct gpio_desc scl;
struct gpio_desc sda;
+};
+static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int failures = 0;
PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -ENODEV;
-EIO I think
}
while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
}
send_stop(scl, sda, delay);
return failures;
+}
+static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */
while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
send_stop(scl, sda, delay);
return 0;
+}
+static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
int ret;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD) {
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
msg->len);
} else {
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
msg->len, next_is_read);
}
if (ret)
return -EREMOTEIO;
}
return 0;
+}
+static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int ret;
send_start(scl, sda, delay);
ret = write_byte(scl, sda, delay, (chip << 1) | 0);
send_stop(scl, sda, delay);
PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
return ret;
+}
+static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
i2c_bus->speed = speed;
i2c_bus->delay = lldiv(1000000, speed << 2);
(scl, sda, i2c_bus->delay);
PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
__func__, dev->seq, dev->name, speed, i2c_bus->delay);
return 0;
+}
+static int soft_i2c_ofdata_to_platdata(struct udevice *dev) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
char *pin_name;
int ret;
pin_name = "clock-pin";
nit: You don't need this variable
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->scl, GPIOD_IS_OUT);
You should not use the nodev version when you have a device. Also below.
if (ret)
goto error;
pin_name = "data-pin";
nit: or here
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->sda, GPIOD_IS_OUT);
if (ret)
goto error;
PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
return 0;
+error:
error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
return ret;
+}
+static const struct dm_i2c_ops soft_i2c_ops = {
.xfer = soft_i2c_xfer,
.probe_chip = soft_i2c_probe,
.set_bus_speed = soft_i2c_set_bus_speed,
+};
+static const struct udevice_id soft_i2c_ids[] = {
{ .compatible = "soft-i2c" },
{ }
+};
+U_BOOT_DRIVER(soft_i2c) = {
.name = "soft-i2c",
.id = UCLASS_I2C,
.of_match = soft_i2c_ids,
.ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
.ops = &soft_i2c_ops,
+};
+#endif /* CONFIG_DM_I2C_SOFT */
1.9.1
Regards, Simon

Hello,
On 03/24/2015 07:01 AM, Heiko Schocher wrote:
Hello Simon, Przemyslaw,
Am 24.03.2015 00:38, schrieb Simon Glass:
Hi Przemyslaw,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>; /* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] };
};
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Is the intention to retire the old (non-driver-model) code? What boards use it?
I think not ... Hmm ... maybe you move the new code into a new file?
Ok, I will do this for v2.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Hups... seems I missed this patches ...
drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
Thanks for this work!
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
@@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h>
- # ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif
@@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif
+# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \
do { } while (0)
+# endif
+# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif
+# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \
do { \
if (bit) { \
sda->flags &= ~GPIOD_IS_OUT; \
sda->flags |= GPIOD_IS_IN; \
dm_gpio_set_dir(sda); \
} else { \
sda->flags &= ~GPIOD_IS_IN; \
sda->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(sda); \
dm_gpio_set_value(sda, 0); \
} \
I2C_GPIO_SYNC(sda); \
} while (0)
+# endif
+# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \
do { \
scl->flags &= ~GPIOD_IS_IN; \
scl->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(scl); \
dm_gpio_set_value(scl, bit); \
I2C_GPIO_SYNC(scl); \
} while (0)
+# endif
+# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif
+#endif /* CONFIG_DM_I2C_SOFT */
/*-----------------------------------------------------------------------
- Definitions
*/ @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
- #ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \
@@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR;
/*-----------------------------------------------------------------------
- Local functions
*/ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
- #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
/*-----------------------------------------------------------------------
- Send a reset sequence consisting of 9 clocks with the data
signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
+#ifdef I2C_INIT
I2C_INIT(scl, sda);
+#endif
I2C_TRISTATE(sda);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
}
send_stop(scl, sda, delay);
I2C_TRISTATE(sda);
For the new code I would much prefer that these become functions rather than macros. What is the benefit of using a macro?
I do not know the real reason, but I think it comes from powerpc times, where we had not yet such powerful SoCs and it saved some instructions ... I tend to say, lets move the dm part into a new file ... and let it us do like linux ...
@Simon: Do you pick up this patches through DM tree, or should I pick them up?
bye, Heiko
Yes, I will make the new file.
Best regards,

Hello Simon,
On 03/24/2015 12:38 AM, Simon Glass wrote:
Hi Przemyslaw,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
This change adds driver model support to software emulated i2c bus driver. To bind the driver, proper device-tree node must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles
/* Define the alias number to keep bus numbering order */ aliases { [...] i2c5 = "/i2c@13500000"; i2c6 = "/soft-i2c@1"; [...] };
/* Define the basic bus attributes */ soft-i2c@1 { #address-cells = <1>; #size-cells = <0>; compatible = "soft-i2c"; clock-frequency = <50000>;
/* Define the proper GPIO pins */ clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>; /* Optionally, define some driver node (bus child) */ somedev@0x44 { compatible = "somedev"; reg = <0x44>; [...] };
};
The device can be accessed by the i2c command: i2c dev 8 (bus number set by alias) i2c probe <0x44> (address is optionally) i2c md 0x44 0x0 (dump dev registers at address 0x0) Valid chip addresses: 0x44 (success!) ...
The previous driver functionality stays unchanged. Driving the bus lines is done by dm gpio calls in the preprocessor macros. Each, can be redefined by the user as previous.
Tested on Trats2 and Odroid U3.
Is the intention to retire the old (non-driver-model) code? What boards use it?
Yes, it is in the intention. There is about 20 boards, with defined soft-i2c macros in their configs.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..7039b6d 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index db9b402..7afae0b 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,7 @@ /*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
@@ -14,6 +17,11 @@ */
#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ #include <ioports.h> #include <asm/io.h> @@ -32,11 +40,9 @@ #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) #include <asm/io.h> #endif -#include <i2c.h>
+#if defined(CONFIG_SYS_I2C) #if defined(CONFIG_SOFT_I2C_GPIO_SCL) -# include <asm/gpio.h>
- # ifndef I2C_GPIO_SYNC # define I2C_GPIO_SYNC # endif
@@ -85,6 +91,7 @@ # endif
#endif +#endif
/* #define DEBUG_I2C */
@@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE #endif
+/* DM SOFT I2C */ +#ifdef CONFIG_DM_I2C_SOFT +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC(gpio) +# endif
+# ifndef I2C_INIT +# define I2C_INIT(scl, sda) \
do { } while (0)
+# endif
+# ifndef I2C_ACTIVE +# define I2C_ACTIVE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_TRISTATE +# define I2C_TRISTATE(sda) \
do { } while (0)
+# endif
+# ifndef I2C_READ +# define I2C_READ(sda) dm_gpio_get_value(sda); +# endif
+# ifndef I2C_SDA +# define I2C_SDA(sda, bit) \
do { \
if (bit) { \
sda->flags &= ~GPIOD_IS_OUT; \
sda->flags |= GPIOD_IS_IN; \
dm_gpio_set_dir(sda); \
} else { \
sda->flags &= ~GPIOD_IS_IN; \
sda->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(sda); \
dm_gpio_set_value(sda, 0); \
} \
I2C_GPIO_SYNC(sda); \
} while (0)
+# endif
+# ifndef I2C_SCL +# define I2C_SCL(scl, bit) \
do { \
scl->flags &= ~GPIOD_IS_IN; \
scl->flags |= GPIOD_IS_OUT; \
dm_gpio_set_dir(scl); \
dm_gpio_set_value(scl, bit); \
I2C_GPIO_SYNC(scl); \
} while (0)
+# endif
+# ifndef I2C_DELAY +# define I2C_DELAY(us) udelay(us) /* 1/4 I2C clock duration */ +# endif
+#endif /* CONFIG_DM_I2C_SOFT */
- /*-----------------------------------------------------------------------
*/
- Definitions
@@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR; #define I2C_ACK 0 /* PD_SDA level to ack a byte */ #define I2C_NOACK 1 /* PD_SDA level to noack a byte */
- #ifdef DEBUG_I2C #define PRINTD(fmt,args...) do { \ printf (fmt ,##args); \
@@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR; /*-----------------------------------------------------------------------
- Local functions
*/ +#ifdef CONFIG_SYS_I2C #if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +static void send_reset(void); +#endif +static void send_start(void); +static void send_stop(void); +static void send_ack(int); +static int write_byte(uchar byte); +static uchar read_byte(int); +#else +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
- #if !defined(CONFIG_SYS_I2C_INIT_BOARD) /*-----------------------------------------------------------------------
*/
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
+#ifdef CONFIG_SYS_I2C_SOFT static void send_reset(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -166,11 +240,36 @@ static void send_reset(void) send_stop(); I2C_TRISTATE; } +#else +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
+#ifdef I2C_INIT
I2C_INIT(scl, sda);
+#endif
I2C_TRISTATE(sda);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
}
send_stop(scl, sda, delay);
I2C_TRISTATE(sda);
For the new code I would much prefer that these become functions rather than macros. What is the benefit of using a macro?
Right, there are probably no benefits of keeping old code style.
+} +#endif /* CONFIG_SYS_I2C_SOFT */ #endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_start(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -184,10 +283,25 @@ static void send_start(void) I2C_SDA(0); I2C_DELAY; } +#else +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- STOP: Low -> High on SDA while SCL is High
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_stop(void) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -203,10 +317,28 @@ static void send_stop(void) I2C_DELAY; I2C_TRISTATE; } +#else +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_ACTIVE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_TRISTATE(sda);
+} +#endif /* CONFIG_SYS_I2C_SOFT */
/*-----------------------------------------------------------------------
- ack should be I2C_ACK or I2C_NOACK
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void send_ack(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -222,10 +354,29 @@ static void send_ack(int ack) I2C_SCL(0); I2C_DELAY; } +#else +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
I2C_SDA(sda, ack);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
+} +#endif
/*-----------------------------------------------------------------------
- Send 8 bits and look for an acknowledgement.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int write_byte(uchar data) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -263,11 +414,52 @@ static int write_byte(uchar data)
return(nack); /* not a nack is an ack */
} +#else +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int j;
int nack;
I2C_ACTIVE(sda);
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
data <<= 1;
}
/*
* Look for an <ACK>(negative logic) and return it.
*/
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_TRISTATE(sda);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
nack = I2C_READ(sda);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_ACTIVE(sda);
return nack; /* not a nack is an ack */
+} +#endif
/*-----------------------------------------------------------------------
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
*/ +#ifdef CONFIG_SYS_I2C_SOFT static uchar read_byte(int ack) { I2C_SOFT_DECLARATIONS /* intentional without ';' */ @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
return(data);
} +#else +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SOFT_DECLARATIONS /* intentional without ';' */
int data;
int j;
/*
* Read 8 bits, MSB first.
*/
I2C_TRISTATE(sda);
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+} +#endif /* CONFIG_SYS_I2C_SOFT */ /*-----------------------------------------------------------------------
- Initialization
*/ +#ifdef CONFIG_SYS_I2C_SOFT static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { #if defined(CONFIG_SYS_I2C_INIT_BOARD) @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) send_reset (); #endif } +#endif
/*-----------------------------------------------------------------------
- Probe to see if a chip is present. Also good for checking for the
- completion of EEPROM writes since the chip stops responding until
- the write completes (typically 10mSec).
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr) { int rc; @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
return (rc ? 1 : 0);
} +#endif
/*-----------------------------------------------------------------------
- Read bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -409,10 +633,12 @@ static int soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(0); } +#endif
/*-----------------------------------------------------------------------
- Write bytes
*/ +#ifdef CONFIG_SYS_I2C_SOFT static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, int alen, uchar *buffer, int len) { @@ -444,10 +670,12 @@ static int soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, send_stop(); return(failures); } +#endif
/*
- Register soft i2c adapters
*/ +#ifdef CONFIG_SYS_I2C_SOFT U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, soft_i2c_read, soft_i2c_write, NULL, CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, CONFIG_SYS_I2C_SOFT_SLAVE_4, 3) #endif +#endif /* CONFIG_SYS_I2C_SOFT */
+#ifdef CONFIG_DM_I2C_SOFT +struct soft_i2c_bus {
unsigned int speed;
unsigned long delay;
struct gpio_desc scl;
struct gpio_desc sda;
+};
+static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int failures = 0;
PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -ENODEV;
-EIO I think
Ok, will fix.
}
while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
}
send_stop(scl, sda, delay);
return failures;
+}
+static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
uchar *buffer, int len)
+{
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */
while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
send_stop(scl, sda, delay);
return 0;
+}
+static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
int ret;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD) {
ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
msg->len);
} else {
ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
msg->len, next_is_read);
}
if (ret)
return -EREMOTEIO;
}
return 0;
+}
+static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
unsigned int delay = i2c_bus->delay;
int ret;
send_start(scl, sda, delay);
ret = write_byte(scl, sda, delay, (chip << 1) | 0);
send_stop(scl, sda, delay);
PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
return ret;
+}
+static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
struct gpio_desc *scl = &i2c_bus->scl;
struct gpio_desc *sda = &i2c_bus->sda;
i2c_bus->speed = speed;
i2c_bus->delay = lldiv(1000000, speed << 2);
(scl, sda, i2c_bus->delay);
PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
__func__, dev->seq, dev->name, speed, i2c_bus->delay);
return 0;
+}
+static int soft_i2c_ofdata_to_platdata(struct udevice *dev) +{
struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
char *pin_name;
int ret;
pin_name = "clock-pin";
nit: You don't need this variable
It is used when printing the error, at the end of this function.
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->scl, GPIOD_IS_OUT);
You should not use the nodev version when you have a device. Also below.
Ah, right. I made this mistake also in last pmic patches. Will fix for both.
if (ret)
goto error;
pin_name = "data-pin";
nit: or here
ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
0, &i2c_bus->sda, GPIOD_IS_OUT);
if (ret)
goto error;
PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
return 0;
+error:
error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
return ret;
+}
+static const struct dm_i2c_ops soft_i2c_ops = {
.xfer = soft_i2c_xfer,
.probe_chip = soft_i2c_probe,
.set_bus_speed = soft_i2c_set_bus_speed,
+};
+static const struct udevice_id soft_i2c_ids[] = {
{ .compatible = "soft-i2c" },
{ }
+};
+U_BOOT_DRIVER(soft_i2c) = {
.name = "soft-i2c",
.id = UCLASS_I2C,
.of_match = soft_i2c_ids,
.ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
.ops = &soft_i2c_ops,
+};
+#endif /* CONFIG_DM_I2C_SOFT */
1.9.1
Regards, Simon
Best regards,

Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de --- drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..0a52ed9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,7 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help - Enable driver model for I2C. This SPI flash interface - (spi_flash_probe(), spi_flash_write(), etc.) is then - implemented by the SPI flash uclass. There is one standard - SPI flash driver which knows how to probe most chips - supported by U-Boot. The uclass interface is defined in - include/spi_flash.h, but is currently fully compatible - with the old interface to avoid confusion and duplication - during the transition parent. SPI and SPI flash must be - enabled together (it is not possible to use driver model - for one and not the other). + Enable driver model for I2C.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer"

Hi Przemyslaw,
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..0a52ed9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,7 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer"
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

Hi,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..0a52ed9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,7 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C.
That's too short IMO :-)
Can you add a few more details along the lines of what is described for SPI, and also mention the COMPAT option?
Thanks for fixing this.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer" -- 1.9.1
Regards, Simon

Hello Simon,
On 03/24/2015 12:39 AM, Simon Glass wrote:
Hi,
On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..0a52ed9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,7 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C.
That's too short IMO :-)
Can you add a few more details along the lines of what is described for SPI, and also mention the COMPAT option?
Thanks for fixing this.
Ok, will add more details :)
config DM_I2C_COMPAT bool "Enable I2C compatibility layer" -- 1.9.1
Regards, Simon
Best regards,

Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de --- drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0a52ed9..dd7eb3c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -13,6 +13,49 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_SOFT + bool "Enable Driver Model for Software I2C Driver" + depends on DM_I2C + help + Enable the i2c bus driver emulation by using GPIO. + The bus configuration is given by the device-tree, like below. + + /* First, define the alias number to have continuous bus numbering */ + aliases { + [...] + i2c5 = "/i2c@13500000"; + i2c6 = "/soft-i2c@1"; + [...] + } + + /* And next define the basic bus attributes */ + soft-i2c@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "soft-i2c"; + clock-frequency = <50000>; + /* Define the proper GPIO pins */ + clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>; + data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>; + + /* Optionally, define some driver node (bus child) */ + somedev@0x44 { + compatible = "somedev"; + reg = <0x44>; + [...] + }; + } + + The device can be accessed by the i2c command: + # i2c dev 8 (bus number set by alias) + # i2c probe <0x44> (address is optionally) + # i2c md 0x44 0x0 (dump dev registers at address 0x0) + # Valid chip addresses: 0x44 (success!) + ... + + Driving the bus lines is done by dm gpio calls in the preprocessor + macros. Each, can be redefined by the user. + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C

Hi Przemyslaw,
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0a52ed9..dd7eb3c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -13,6 +13,49 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_SOFT
- bool "Enable Driver Model for Software I2C Driver"
- depends on DM_I2C
- help
Enable the i2c bus driver emulation by using GPIO.
The bus configuration is given by the device-tree, like
below. +
/* First, define the alias number to have continuous bus
numbering */
aliases {
[...]
i2c5 = "/i2c@13500000";
i2c6 = "/soft-i2c@1";
[...]
}
/* And next define the basic bus attributes */
soft-i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "soft-i2c";
clock-frequency = <50000>;
/* Define the proper GPIO pins */
clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
/* Optionally, define some driver node (bus child) */
somedev@0x44 {
compatible = "somedev";
reg = <0x44>;
[...]
};
}
The device can be accessed by the i2c command:
# i2c dev 8 (bus number set by alias)
# i2c probe <0x44> (address is optionally)
# i2c md 0x44 0x0 (dump dev registers at
address 0x0)
# Valid chip addresses: 0x44 (success!)
...
Driving the bus lines is done by dm gpio calls in the
preprocessor
macros. Each, can be redefined by the user.
config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C
Reviewed-by: Lukasz Majewski l.majewski@samsung.com

On 10 March 2015 at 04:30, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
This is very nice.
Acked-by: Simon Glass sjg@chromium.org

Hi.
2015-03-10 19:30 GMT+09:00 Przemyslaw Marczak p.marczak@samsung.com:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com
I am no longer working for Panasonic. The old email address will get unavailable at the end of March.
Going forward, please use my new address, yamada.masahiro@socionext.com
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0a52ed9..dd7eb3c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -13,6 +13,49 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_SOFT
bool "Enable Driver Model for Software I2C Driver"
depends on DM_I2C
help
Enable the i2c bus driver emulation by using GPIO.
Very nice!
The bus configuration is given by the device-tree, like below.
/* First, define the alias number to have continuous bus numbering */
aliases {
[...]
i2c5 = "/i2c@13500000";
i2c6 = "/soft-i2c@1";
[...]
}
This description is not specific to this CONFIG option.
The relation between the aliases node and the sequence number is well-documented in doc/driver-model/README.txt.
Should we repeat it here?
/* And next define the basic bus attributes */
soft-i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "soft-i2c";
clock-frequency = <50000>;
/* Define the proper GPIO pins */
clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
/* Optionally, define some driver node (bus child) */
somedev@0x44 {
compatible = "somedev";
reg = <0x44>;
[...]
};
}
This is binding information, right?
Stuff like that is usually documented in a separate text file.
In Linux, Documentation/devicetree/bindings/i2c/ In U-boot, doc/device-tree-bindings/i2c/
The device can be accessed by the i2c command:
# i2c dev 8 (bus number set by alias)
# i2c probe <0x44> (address is optionally)
# i2c md 0x44 0x0 (dump dev registers at address 0x0)
# Valid chip addresses: 0x44 (success!)
...
This is the usage of "i2c" command. It is not specific to this option, either.
Driving the bus lines is done by dm gpio calls in the preprocessor
macros. Each, can be redefined by the user.
config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C -- 1.9.1
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Hello Masahiro,
On 03/25/2015 04:35 AM, Masahiro Yamada wrote:
Hi.
2015-03-10 19:30 GMT+09:00 Przemyslaw Marczak p.marczak@samsung.com:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.m@jp.panasonic.com
I am no longer working for Panasonic. The old email address will get unavailable at the end of March.
Going forward, please use my new address, yamada.masahiro@socionext.com
Ok, will update this.
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 0a52ed9..dd7eb3c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -13,6 +13,49 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_SOFT
bool "Enable Driver Model for Software I2C Driver"
depends on DM_I2C
help
Enable the i2c bus driver emulation by using GPIO.
Very nice!
The bus configuration is given by the device-tree, like below.
/* First, define the alias number to have continuous bus numbering */
aliases {
[...]
i2c5 = "/i2c@13500000";
i2c6 = "/soft-i2c@1";
[...]
}
This description is not specific to this CONFIG option.
The relation between the aliases node and the sequence number is well-documented in doc/driver-model/README.txt.
Should we repeat it here?
Yes, you are right. I wanted to put here all informations, required to make it working with devices, since I think it could facilitate the development for the others. Will remove this.
/* And next define the basic bus attributes */
soft-i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "soft-i2c";
clock-frequency = <50000>;
/* Define the proper GPIO pins */
clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
/* Optionally, define some driver node (bus child) */
somedev@0x44 {
compatible = "somedev";
reg = <0x44>;
[...]
};
}
This is binding information, right?
Stuff like that is usually documented in a separate text file.
In Linux, Documentation/devicetree/bindings/i2c/ In U-boot, doc/device-tree-bindings/i2c/
Right, will move into proper path.
The device can be accessed by the i2c command:
# i2c dev 8 (bus number set by alias)
# i2c probe <0x44> (address is optionally)
# i2c md 0x44 0x0 (dump dev registers at address 0x0)
# Valid chip addresses: 0x44 (success!)
...
This is the usage of "i2c" command. It is not specific to this option, either.
Ok.
Driving the bus lines is done by dm gpio calls in the preprocessor
macros. Each, can be redefined by the user.
- config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C
-- 1.9.1
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Thanks for review!
Best regards,

This patchset enables driver model support for software i2c bus driver. It was tested on Trats2 and Odroid U3 devices.
It can be tested on any other device by just modifying the dts file, first by disabling the hardware i2c bus and then, as it is described in the doc binding file, add i2c-gpio node.
The drivers, which are using the old api are not converted with this patchset. I hope that maintainers will do this if required.
Probably the software i2c is used for PMIC devices not only for Trats2, or Universal C210, so I suggest to wait with moving the drivers until the pmic is done - this will prevent adding temporary code.
Przemyslaw Marczak (3): dm: gpio: request list: return the count if requests max_count reached Kconfig: i2c: fix help message related to dm i2c dm: i2c: add i2c-gpio driver
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/gpio/gpio-uclass.c | 6 +- drivers/i2c/Kconfig | 26 ++- drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 353 ++++++++++++++++++++++++++++++ 5 files changed, 408 insertions(+), 15 deletions(-) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c

The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org --- drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index a69bbd2..4b63025 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -590,11 +590,7 @@ int gpio_request_list_by_name_nodev(const void *blob, int node, int count; int ret;
- for (count = 0; ; count++) { - if (count >= max_count) { - ret = -ENOSPC; - goto err; - } + for (count = 0; count < max_count; count++) { ret = _gpio_request_by_name_nodev(blob, node, list_name, count, &desc[count], flags, true); if (ret == -ENOENT)

On 27 March 2015 at 11:33, Przemyslaw Marczak p.marczak@samsung.com wrote:
The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org
drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
Acked-by: Simon Glass sjg@chromium.org

Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2: - add i2c uclass description --- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..979522f 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help - Enable driver model for I2C. This SPI flash interface - (spi_flash_probe(), spi_flash_write(), etc.) is then - implemented by the SPI flash uclass. There is one standard - SPI flash driver which knows how to probe most chips - supported by U-Boot. The uclass interface is defined in - include/spi_flash.h, but is currently fully compatible - with the old interface to avoid confusion and duplication - during the transition parent. SPI and SPI flash must be - enabled together (it is not possible to use driver model - for one and not the other). + Enable driver model for I2C. The I2C uclass interface: probe, read, + write or speed, is implemented with the bus drivers uclass operations, + which provides methods for bus setting and data transfer. Each chip + device (bus child) info is kept as parent platdata. The interface is + defined in include/i2c.h. When i2c bus driver supports the i2c uclass, + but the device drivers not, then DM_I2C_COMPAT config can be used as + compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer"

Hi Przemyslaw,
On 27 March 2015 at 11:33, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- add i2c uclass description
drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
Acked-by: Simon Glass sjg@chromium.org
(see nit below)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..979522f 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C. The I2C uclass interface: probe, read,
write or speed, is implemented with the bus drivers uclass operations,
which provides methods for bus setting and data transfer. Each chip
Can we tweak this a bit?
write and speed, is implemented with the bus drivers' operations, which provide methods for bus setting and data transfer. Each chip
device (bus child) info is kept as parent platdata. The interface is
defined in include/i2c.h. When i2c bus driver supports the i2c uclass,
but the device drivers not, then DM_I2C_COMPAT config can be used as
compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer" -- 1.9.1
Regards, Simon

Hello Simon,
On 03/28/2015 04:08 PM, Simon Glass wrote:
Hi Przemyslaw,
On 27 March 2015 at 11:33, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- add i2c uclass description
drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
Acked-by: Simon Glass sjg@chromium.org
(see nit below)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..979522f 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C. The I2C uclass interface: probe, read,
write or speed, is implemented with the bus drivers uclass operations,
which provides methods for bus setting and data transfer. Each chip
Can we tweak this a bit?
write and speed, is implemented with the bus drivers' operations, which provide methods for bus setting and data transfer. Each chip
Ok, will corrent this.
device (bus child) info is kept as parent platdata. The interface is
defined in include/i2c.h. When i2c bus driver supports the i2c uclass,
but the device drivers not, then DM_I2C_COMPAT config can be used as
compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer"
-- 1.9.1
Regards, Simon
Best regards,

This commit adds driver model support to software emulated i2c bus driver. The device-tree node is compatible with the kernel i2c-gpio driver. It means, that boards device-tree "i2c-gpio" node, should be the same as in the kernel. For operating, it requires proper compatible and gpio pins dts description. Added: - Config: CONFIG_DM_I2C_GPIO - File: drivers/i2c/i2c-gpio.c - File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes: - update comments style, - move preprocesor macros into functions, - add device-tree support, - add driver model i2c support. - add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2: - new file for software i2c driver: i2c-gpio.c - update driver naming: use of "i2c-gpio" - add full compatibility with the kernels device-tree "i2c-gpio" node - fix Kconfig entry --- doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 353 ++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
diff --git a/doc/device-tree-bindings/i2c/i2c-gpio.txt b/doc/device-tree-bindings/i2c/i2c-gpio.txt new file mode 100644 index 0000000..3978381 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-gpio.txt @@ -0,0 +1,37 @@ +I2C gpio device binding +======================= + +Driver: +- drivers/i2c/i2c-gpio.c + +Software i2c device-tree node properties: +Required: +* #address-cells = <1>; +* #size-cells = <0>; +* compatible = "i2c-gpio"; +* gpios = <sda ...>, <scl ...>; + +Optional: +* i2c-gpio,delay-us = <5>; + The resulting transfer speed can be adjusted by setting the delay[us] + between gpio-toggle operations. Speed [Hz] = 1us / 4 * udelay, + It not defined, then default is 5us (~50KHz). + +Example: + +i2c-gpio@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "i2c-gpio"; + gpios = <&gpd1 0 GPIO_ACTIVE_HIGH>, /* SDA */ + <&gpd1 1 GPIO_ACTIVE_HIGH>; /* CLK */ + + i2c-gpio,delay-us = <5>; + + some_device@5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 979522f..22e4a7c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -19,6 +19,15 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_GPIO + bool "Enable Driver Model for software emulated I2C bus driver" + depends on DM_I2C && DM_GPIO + help + Enable the i2c bus driver emulation by using the GPIO. + The bus gpio configuration is given by the device-tree. + Kernel style device-tree node for i2c-gpio is supported. + Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..d9e9f3a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c new file mode 100644 index 0000000..8e9ed6b --- /dev/null +++ b/drivers/i2c/i2c-gpio.c @@ -0,0 +1,353 @@ +/* + * (C) Copyright 2015, Samsung Electronics + * Przemyslaw Marczak p.marczak@samsung.com + * Add driver-model support as a separated driver file + * + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h> + +#define DEFAULT_UDELAY 5 +#define RETRIES 0 +#define I2C_ACK 0 +#define I2C_NOACK 1 + +#ifdef DEBUG_I2C +#define PRINTD(fmt, args...) do { \ + printf (fmt, ##args); \ + } while (0) +#else +#define PRINTD(fmt, args...) +#endif + +DECLARE_GLOBAL_DATA_PTR; + +enum { + PIN_SDA = 0, + PIN_SCL, +}; + +struct i2c_gpio_bus { + unsigned int speed; + unsigned long udelay; + struct gpio_desc gpios[2]; /* sda, scl */ +}; + +/* Local functions */ +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int); + +static int I2C_READ(struct gpio_desc *sda) +{ + return dm_gpio_get_value(sda); +} + +static void I2C_SDA(struct gpio_desc *sda, int bit) +{ + if (bit) { + dm_gpio_set_dir_flags(sda, GPIOD_IS_IN); + } else { + dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT); + dm_gpio_set_value(sda, 0); + } +} + +static void I2C_SCL(struct gpio_desc *scl, int bit) +{ + dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT); + dm_gpio_set_value(scl, bit); +} + +static void I2C_DELAY(unsigned long us) +{ + udelay(us); /* 1/4 I2C clock duration */ +} + +/** + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * <stop> at the end of the sequence for belts & suspenders. + */ +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + int j; + + I2C_SCL(scl, 1); + I2C_SDA(sda, 1); + + for (j = 0; j < 9; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + } + send_stop(scl, sda, delay); +} + +/* START: High -> Low on SDA while SCL is High */ +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_SDA(sda, 0); + I2C_DELAY(delay); +} + +/* STOP: Low -> High on SDA while SCL is High */ +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{ + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, 0); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_DELAY(delay); +} + +/* ack should be I2C_ACK or I2C_NOACK */ +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, ack); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + I2C_SCL(scl, 0); + I2C_DELAY(delay); +} + +/* Send 8 bits and look for an acknowledgement */ +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, uchar data) +{ + int j; + int nack; + + for (j = 0; j < 8; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, data & 0x80); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + + data <<= 1; + } + + /* Look for an <ACK>(negative logic) and return it */ + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SDA(sda, 1); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + I2C_DELAY(delay); + nack = I2C_READ(sda); + I2C_SCL(scl, 0); + I2C_DELAY(delay); + + return nack; /* not a nack is an ack */ +} + +/** + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + int data; + int j; + + /* Read 8 bits, MSB first */ + I2C_SDA(sda, 1); + data = 0; + for (j = 0; j < 8; j++) { + I2C_SCL(scl, 0); + I2C_DELAY(delay); + I2C_SCL(scl, 1); + I2C_DELAY(delay); + data <<= 1; + data |= I2C_READ(sda); + I2C_DELAY(delay); + } + send_ack(scl, sda, delay, ack); + + return data; +} + +static int i2c_write_data(struct i2c_gpio_bus *bus, uchar chip, + uchar *buffer, int len, bool end_with_repeated_start) +{ + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + int failures = 0; + + PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len); + + send_start(scl, sda, delay); + if (write_byte(scl, sda, delay, chip << 1)) { + send_stop(scl, sda, delay); + PRINTD("i2c_write, no chip responded %02X\n", chip); + return -EIO; + } + + while (len-- > 0) { + if (write_byte(scl, sda, delay, *buffer++)) + failures++; + } + + send_stop(scl, sda, delay); + + return failures; +} + +static int i2c_read_data(struct i2c_gpio_bus *bus, uchar chip, + uchar *buffer, int len) +{ + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + + PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len); + + send_start(scl, sda, delay); + write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */ + + while (len-- > 0) + *buffer++ = read_byte(scl, sda, delay, len == 0); + + send_stop(scl, sda, delay); + + return 0; +} + +static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + int ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + if (msg->flags & I2C_M_RD) + ret = i2c_read_data(bus, msg->addr, msg->buf, msg->len); + else + ret = i2c_write_data(bus, msg->addr, msg->buf, msg->len, + next_is_read); + + if (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + int ret; + + send_start(scl, sda, delay); + ret = write_byte(scl, sda, delay, (chip << 1) | 0); + send_stop(scl, sda, delay); + + PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n", + __func__, dev->seq, dev->name, chip, chip_flags, ret); + + return ret; +} + +static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + + bus->speed = speed; + bus->udelay = lldiv(1000000, speed << 2); + + send_reset(scl, sda, bus->udelay); + + PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n", + __func__, dev->seq, dev->name, speed, bus->udelay); + + return 0; +} + +static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + int ret; + + ret = gpio_request_list_by_name(dev, "gpios", bus->gpios, + ARRAY_SIZE(bus->gpios), 0); + if (ret < 0) + goto error; + + bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us", + DEFAULT_UDELAY); + + PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name); + + return 0; +error: + error("Can't get %s gpios! Error: %d", dev->name, ret); + return ret; +} + +static const struct dm_i2c_ops i2c_gpio_ops = { + .xfer = i2c_gpio_xfer, + .probe_chip = i2c_gpio_probe, + .set_bus_speed = i2c_gpio_set_bus_speed, +}; + +static const struct udevice_id i2c_gpio_ids[] = { + { .compatible = "i2c-gpio" }, + { } +}; + +U_BOOT_DRIVER(i2c_gpio) = { + .name = "i2c-gpio", + .id = UCLASS_I2C, + .of_match = i2c_gpio_ids, + .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), + .ops = &i2c_gpio_ops, +};

Hi Przemyslaw,
On 27 March 2015 at 11:33, Przemyslaw Marczak p.marczak@samsung.com wrote:
This commit adds driver model support to software emulated i2c bus driver. The device-tree node is compatible with the kernel i2c-gpio driver. It means, that boards device-tree "i2c-gpio" node, should be the same as in the kernel. For operating, it requires proper compatible and gpio pins dts description. Added:
- Config: CONFIG_DM_I2C_GPIO
- File: drivers/i2c/i2c-gpio.c
- File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes:
- update comments style,
- move preprocesor macros into functions,
- add device-tree support,
- add driver model i2c support.
- add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- new file for software i2c driver: i2c-gpio.c
- update driver naming: use of "i2c-gpio"
- add full compatibility with the kernels device-tree "i2c-gpio" node
- fix Kconfig entry
Sorry I still have a few more comments.
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 353 ++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
diff --git a/doc/device-tree-bindings/i2c/i2c-gpio.txt b/doc/device-tree-bindings/i2c/i2c-gpio.txt new file mode 100644 index 0000000..3978381 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-gpio.txt @@ -0,0 +1,37 @@ +I2C gpio device binding +=======================
+Driver: +- drivers/i2c/i2c-gpio.c
+Software i2c device-tree node properties: +Required: +* #address-cells = <1>; +* #size-cells = <0>; +* compatible = "i2c-gpio"; +* gpios = <sda ...>, <scl ...>;
+Optional: +* i2c-gpio,delay-us = <5>;
- The resulting transfer speed can be adjusted by setting the delay[us]
- between gpio-toggle operations. Speed [Hz] = 1us / 4 * udelay,
- It not defined, then default is 5us (~50KHz).
+Example:
+i2c-gpio@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "i2c-gpio";
gpios = <&gpd1 0 GPIO_ACTIVE_HIGH>, /* SDA */
<&gpd1 1 GPIO_ACTIVE_HIGH>; /* CLK */
i2c-gpio,delay-us = <5>;
some_device@5 {
compatible = "some_device";
reg = <0x5>;
...
};
+}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 979522f..22e4a7c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -19,6 +19,15 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_GPIO
bool "Enable Driver Model for software emulated I2C bus driver"
depends on DM_I2C && DM_GPIO
help
Enable the i2c bus driver emulation by using the GPIO.
by using GPIOs.
The bus gpio configuration is given by the device-tree.
GPIO
device tree (no hypen)
Kernel style device-tree node for i2c-gpio is supported.
Kernel-style device tree bindings are supported
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..d9e9f3a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c new file mode 100644 index 0000000..8e9ed6b --- /dev/null +++ b/drivers/i2c/i2c-gpio.c @@ -0,0 +1,353 @@ +/*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- Add driver-model support as a separated driver file
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
- (C) Copyright 2001, 2002
- Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- SPDX-License-Identifier: GPL-2.0+
- This has been changed substantially by Gerald Van Baren, Custom IDEAS,
- vanbaren@cideas.com. It was heavily influenced by LiMon, written by
- Neil Russell.
- */
+#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h>
+#define DEFAULT_UDELAY 5 +#define RETRIES 0 +#define I2C_ACK 0 +#define I2C_NOACK 1
+#ifdef DEBUG_I2C +#define PRINTD(fmt, args...) do { \
printf (fmt, ##args); \
} while (0)
+#else +#define PRINTD(fmt, args...) +#endif
I don't see any point in this - how about just using debug() instead?
+DECLARE_GLOBAL_DATA_PTR;
+enum {
PIN_SDA = 0,
PIN_SCL,
PIN_COUNT
+};
Document these members - speed is in HzHz, udelay is the delay for what?
+struct i2c_gpio_bus {
unsigned int speed;
unsigned long udelay;
struct gpio_desc gpios[2]; /* sda, scl */
gpios[PIN_COUNT]
+};
+/* Local functions */ +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int);
If you move send_reset() down a bit can you drop these declarations?
+static int I2C_READ(struct gpio_desc *sda)
Lower case - how about gpio_i2c_read()? Same for others.
+{
return dm_gpio_get_value(sda);
+}
+static void I2C_SDA(struct gpio_desc *sda, int bit) +{
if (bit) {
dm_gpio_set_dir_flags(sda, GPIOD_IS_IN);
I assume the polarity is never set, so this should be OK.
} else {
dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT);
dm_gpio_set_value(sda, 0);
}
+}
+static void I2C_SCL(struct gpio_desc *scl, int bit) +{
dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT);
dm_gpio_set_value(scl, bit);
+}
+static void I2C_DELAY(unsigned long us) +{
udelay(us); /* 1/4 I2C clock duration */
+}
+/**
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
- */
+static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I wonder why we don't do one call with delay * 2?
}
send_stop(scl, sda, delay);
+}
+/* START: High -> Low on SDA while SCL is High */ +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+}
+/* STOP: Low -> High on SDA while SCL is High */ +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
+}
+/* ack should be I2C_ACK or I2C_NOACK */ +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, ack);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
+}
+/* Send 8 bits and look for an acknowledgement */ +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
int j;
int nack;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
This sequence of 7 calls appears a lot in this code. Could it go in a function and be called from various places?
data <<= 1;
}
/* Look for an <ACK>(negative logic) and return it */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
nack = I2C_READ(sda);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
return nack; /* not a nack is an ack */
+}
+/**
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
- */
+static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
int data;
int j;
/* Read 8 bits, MSB first */
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+}
+static int i2c_write_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
+{
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int failures = 0;
PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -EIO;
}
while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
}
send_stop(scl, sda, delay);
return failures;
+}
+static int i2c_read_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len)
+{
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */
while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
send_stop(scl, sda, delay);
return 0;
+}
+static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
int ret;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD)
ret = i2c_read_data(bus, msg->addr, msg->buf, msg->len);
else
ret = i2c_write_data(bus, msg->addr, msg->buf, msg->len,
next_is_read);
if (ret)
return -EREMOTEIO;
}
return 0;
+}
+static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int ret;
send_start(scl, sda, delay);
ret = write_byte(scl, sda, delay, (chip << 1) | 0);
send_stop(scl, sda, delay);
PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
return ret;
+}
+static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
bus->speed = speed;
bus->udelay = lldiv(1000000, speed << 2);
Why lldiv() if the value can never be >100,000? Can we just use normal division?
send_reset(scl, sda, bus->udelay);
PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
__func__, dev->seq, dev->name, speed, bus->udelay);
return 0;
+}
+static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
int ret;
ret = gpio_request_list_by_name(dev, "gpios", bus->gpios,
ARRAY_SIZE(bus->gpios), 0);
if (ret < 0)
goto error;
bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us",
DEFAULT_UDELAY);
PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
return 0;
+error:
error("Can't get %s gpios! Error: %d", dev->name, ret);
return ret;
+}
+static const struct dm_i2c_ops i2c_gpio_ops = {
.xfer = i2c_gpio_xfer,
.probe_chip = i2c_gpio_probe,
.set_bus_speed = i2c_gpio_set_bus_speed,
+};
+static const struct udevice_id i2c_gpio_ids[] = {
{ .compatible = "i2c-gpio" },
{ }
+};
+U_BOOT_DRIVER(i2c_gpio) = {
.name = "i2c-gpio",
.id = UCLASS_I2C,
.of_match = i2c_gpio_ids,
.ofdata_to_platdata = i2c_gpio_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct i2c_gpio_bus),
.ops = &i2c_gpio_ops,
+};
1.9.1
Regards, Simon

Hello Simon,
On 03/28/2015 04:08 PM, Simon Glass wrote:
Hi Przemyslaw,
On 27 March 2015 at 11:33, Przemyslaw Marczak p.marczak@samsung.com wrote:
This commit adds driver model support to software emulated i2c bus driver. The device-tree node is compatible with the kernel i2c-gpio driver. It means, that boards device-tree "i2c-gpio" node, should be the same as in the kernel. For operating, it requires proper compatible and gpio pins dts description. Added:
- Config: CONFIG_DM_I2C_GPIO
- File: drivers/i2c/i2c-gpio.c
- File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes:
- update comments style,
- move preprocesor macros into functions,
- add device-tree support,
- add driver model i2c support.
- add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- new file for software i2c driver: i2c-gpio.c
- update driver naming: use of "i2c-gpio"
- add full compatibility with the kernels device-tree "i2c-gpio" node
- fix Kconfig entry
Sorry I still have a few more comments.
OK, this is the purpose of this list:)
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 353 ++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
diff --git a/doc/device-tree-bindings/i2c/i2c-gpio.txt b/doc/device-tree-bindings/i2c/i2c-gpio.txt new file mode 100644 index 0000000..3978381 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-gpio.txt @@ -0,0 +1,37 @@ +I2C gpio device binding +=======================
+Driver: +- drivers/i2c/i2c-gpio.c
+Software i2c device-tree node properties: +Required: +* #address-cells = <1>; +* #size-cells = <0>; +* compatible = "i2c-gpio"; +* gpios = <sda ...>, <scl ...>;
+Optional: +* i2c-gpio,delay-us = <5>;
- The resulting transfer speed can be adjusted by setting the delay[us]
- between gpio-toggle operations. Speed [Hz] = 1us / 4 * udelay,
- It not defined, then default is 5us (~50KHz).
+Example:
+i2c-gpio@1 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "i2c-gpio";
gpios = <&gpd1 0 GPIO_ACTIVE_HIGH>, /* SDA */
<&gpd1 1 GPIO_ACTIVE_HIGH>; /* CLK */
i2c-gpio,delay-us = <5>;
some_device@5 {
compatible = "some_device";
reg = <0x5>;
...
};
+}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 979522f..22e4a7c 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -19,6 +19,15 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_GPIO
bool "Enable Driver Model for software emulated I2C bus driver"
depends on DM_I2C && DM_GPIO
help
Enable the i2c bus driver emulation by using the GPIO.
by using GPIOs.
The bus gpio configuration is given by the device-tree.
GPIO
device tree (no hypen)
Kernel style device-tree node for i2c-gpio is supported.
Kernel-style device tree bindings are supported
Thanks, will fix the all above.
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
- config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..d9e9f3a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c new file mode 100644 index 0000000..8e9ed6b --- /dev/null +++ b/drivers/i2c/i2c-gpio.c @@ -0,0 +1,353 @@ +/*
- (C) Copyright 2015, Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- Add driver-model support as a separated driver file
- (C) Copyright 2009
- Heiko Schocher, DENX Software Engineering, hs@denx.de.
- Changes for multibus/multiadapter I2C support.
- (C) Copyright 2001, 2002
- Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- SPDX-License-Identifier: GPL-2.0+
- This has been changed substantially by Gerald Van Baren, Custom IDEAS,
- vanbaren@cideas.com. It was heavily influenced by LiMon, written by
- Neil Russell.
- */
+#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <div64.h> +#include <asm/gpio.h>
+#define DEFAULT_UDELAY 5 +#define RETRIES 0 +#define I2C_ACK 0 +#define I2C_NOACK 1
+#ifdef DEBUG_I2C +#define PRINTD(fmt, args...) do { \
printf (fmt, ##args); \
} while (0)
+#else +#define PRINTD(fmt, args...) +#endif
I don't see any point in this - how about just using debug() instead?
Right, will change to debug().
+DECLARE_GLOBAL_DATA_PTR;
+enum {
PIN_SDA = 0,
PIN_SCL,
PIN_COUNT
Ok
+};
Document these members - speed is in HzHz, udelay is the delay for what?
The delay was described in the binding file, I will also add the description beside the structure. And I will remove the speed, since uclass keeps an info about this.
+struct i2c_gpio_bus {
unsigned int speed;
unsigned long udelay;
struct gpio_desc gpios[2]; /* sda, scl */
gpios[PIN_COUNT]
Ok.
+};
+/* Local functions */ +static void send_reset(struct gpio_desc *, struct gpio_desc *, int); +static void send_start(struct gpio_desc *, struct gpio_desc *, int); +static void send_stop(struct gpio_desc *, struct gpio_desc *, int); +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int); +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar); +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int);
If you move send_reset() down a bit can you drop these declarations?
+static int I2C_READ(struct gpio_desc *sda)
Lower case - how about gpio_i2c_read()? Same for others.
Ok, will clean this functions.
+{
return dm_gpio_get_value(sda);
+}
+static void I2C_SDA(struct gpio_desc *sda, int bit) +{
if (bit) {
dm_gpio_set_dir_flags(sda, GPIOD_IS_IN);
I assume the polarity is never set, so this should be OK.
Yes, this works fine.
} else {
dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT);
dm_gpio_set_value(sda, 0);
}
+}
+static void I2C_SCL(struct gpio_desc *scl, int bit) +{
dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT);
dm_gpio_set_value(scl, bit);
+}
+static void I2C_DELAY(unsigned long us) +{
udelay(us); /* 1/4 I2C clock duration */
+}
+/**
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
- */
+static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
int j;
I2C_SCL(scl, 1);
I2C_SDA(sda, 1);
for (j = 0; j < 9; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I wonder why we don't do one call with delay * 2?
Right.
}
send_stop(scl, sda, delay);
+}
+/* START: High -> Low on SDA while SCL is High */ +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
+}
+/* STOP: Low -> High on SDA while SCL is High */ +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay) +{
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
+}
+/* ack should be I2C_ACK or I2C_NOACK */ +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, ack);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
+}
+/* Send 8 bits and look for an acknowledgement */ +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, uchar data)
+{
int j;
int nack;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, data & 0x80);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
This sequence of 7 calls appears a lot in this code. Could it go in a function and be called from various places?
Ok, I will clean this.
data <<= 1;
}
/* Look for an <ACK>(negative logic) and return it */
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SDA(sda, 1);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
I2C_DELAY(delay);
nack = I2C_READ(sda);
I2C_SCL(scl, 0);
I2C_DELAY(delay);
return nack; /* not a nack is an ack */
+}
+/**
- if ack == I2C_ACK, ACK the byte so can continue reading, else
- send I2C_NOACK to end the read.
- */
+static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
int delay, int ack)
+{
int data;
int j;
/* Read 8 bits, MSB first */
I2C_SDA(sda, 1);
data = 0;
for (j = 0; j < 8; j++) {
I2C_SCL(scl, 0);
I2C_DELAY(delay);
I2C_SCL(scl, 1);
I2C_DELAY(delay);
data <<= 1;
data |= I2C_READ(sda);
I2C_DELAY(delay);
}
send_ack(scl, sda, delay, ack);
return data;
+}
+static int i2c_write_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len, bool end_with_repeated_start)
+{
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int failures = 0;
PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
if (write_byte(scl, sda, delay, chip << 1)) {
send_stop(scl, sda, delay);
PRINTD("i2c_write, no chip responded %02X\n", chip);
return -EIO;
}
while (len-- > 0) {
if (write_byte(scl, sda, delay, *buffer++))
failures++;
}
send_stop(scl, sda, delay);
return failures;
+}
+static int i2c_read_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len)
+{
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
send_start(scl, sda, delay);
write_byte(scl, sda, delay, (chip << 1) | 1); /* read cycle */
while (len-- > 0)
*buffer++ = read_byte(scl, sda, delay, len == 0);
send_stop(scl, sda, delay);
return 0;
+}
+static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
int ret;
for (; nmsgs > 0; nmsgs--, msg++) {
bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
if (msg->flags & I2C_M_RD)
ret = i2c_read_data(bus, msg->addr, msg->buf, msg->len);
else
ret = i2c_write_data(bus, msg->addr, msg->buf, msg->len,
next_is_read);
if (ret)
return -EREMOTEIO;
}
return 0;
+}
+static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int ret;
send_start(scl, sda, delay);
ret = write_byte(scl, sda, delay, (chip << 1) | 0);
send_stop(scl, sda, delay);
PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
return ret;
+}
+static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
struct gpio_desc *scl = &bus->gpios[PIN_SCL];
struct gpio_desc *sda = &bus->gpios[PIN_SDA];
bus->speed = speed;
bus->udelay = lldiv(1000000, speed << 2);
Why lldiv() if the value can never be >100,000? Can we just use normal division?
ok, will change this. And I will remove the speed, since it's the same value as uclass provides in struct dm_i2c_bus.
send_reset(scl, sda, bus->udelay);
PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
__func__, dev->seq, dev->name, speed, bus->udelay);
return 0;
+}
+static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) +{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
int ret;
ret = gpio_request_list_by_name(dev, "gpios", bus->gpios,
ARRAY_SIZE(bus->gpios), 0);
if (ret < 0)
goto error;
bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us",
DEFAULT_UDELAY);
PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
return 0;
+error:
error("Can't get %s gpios! Error: %d", dev->name, ret);
return ret;
+}
+static const struct dm_i2c_ops i2c_gpio_ops = {
.xfer = i2c_gpio_xfer,
.probe_chip = i2c_gpio_probe,
.set_bus_speed = i2c_gpio_set_bus_speed,
+};
+static const struct udevice_id i2c_gpio_ids[] = {
{ .compatible = "i2c-gpio" },
{ }
+};
+U_BOOT_DRIVER(i2c_gpio) = {
.name = "i2c-gpio",
.id = UCLASS_I2C,
.of_match = i2c_gpio_ids,
.ofdata_to_platdata = i2c_gpio_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct i2c_gpio_bus),
.ops = &i2c_gpio_ops,
+};
1.9.1
Regards, Simon
Thank you for the review.
Best regards,

This patchset enables driver model support for software i2c bus driver. It was tested on Trats2 and Odroid U3 devices.
It can be tested on any other device by just modifying the dts file, first by disabling the hardware i2c bus and then, as it is described in the doc binding file, add i2c-gpio node.
The drivers, which are using the old api are not converted with this patchset. I hope that maintainers will do this if required.
Probably the software i2c is used for PMIC devices not only for Trats2, or Universal C210, so I suggest to wait with moving the drivers until the pmic is done - this will prevent adding temporary code.
Przemyslaw Marczak (3): dm: gpio: request list: return the count if requests max_count reached Kconfig: i2c: fix help message related to dm i2c dm: i2c: add i2c-gpio driver
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/gpio/gpio-uclass.c | 6 +- drivers/i2c/Kconfig | 26 ++- drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 345 ++++++++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 15 deletions(-) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c

The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org
Changes V3: - new commit --- drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index a69bbd2..4b63025 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -590,11 +590,7 @@ int gpio_request_list_by_name_nodev(const void *blob, int node, int count; int ret;
- for (count = 0; ; count++) { - if (count >= max_count) { - ret = -ENOSPC; - goto err; - } + for (count = 0; count < max_count; count++) { ret = _gpio_request_by_name_nodev(blob, node, list_name, count, &desc[count], flags, true); if (ret == -ENOENT)

On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org
Changes V3:
- new commit
There's something odd about the change logs here - they are coming in as part of the commit message. Are you using patman?
Acked-by: Simon Glass sjg@chromium.org

On 31 March 2015 at 21:17, Simon Glass sjg@chromium.org wrote:
On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org
Changes V3:
- new commit
There's something odd about the change logs here - they are coming in as part of the commit message. Are you using patman?
Acked-by: Simon Glass sjg@chromium.org
Applied to u-boot-dm/next, thanks!

Hello Simon,
On 04/01/2015 05:17 AM, Simon Glass wrote:
On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
The function gpio_request_list_by_name_nodev() returned -ENOSPC error, when the loop count was greater than requested count. This was wrong, because function should return the requested gpio count, when meets the call request without errors. Now, the loop ends on requested max_count.
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Simon Glass sjg@chromium.org
Changes V3:
- new commit
There's something odd about the change logs here - they are coming in as part of the commit message. Are you using patman?
Acked-by: Simon Glass sjg@chromium.org
Sorry for this, it's by mistake, I don't use patman for generating patches. I add change log manually.
Thanks for applying this series :)
Best regards,

Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2: - add i2c uclass description
Changes v3: - Kconfig: i2c-uclass: fix help message --- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..c83a984 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help - Enable driver model for I2C. This SPI flash interface - (spi_flash_probe(), spi_flash_write(), etc.) is then - implemented by the SPI flash uclass. There is one standard - SPI flash driver which knows how to probe most chips - supported by U-Boot. The uclass interface is defined in - include/spi_flash.h, but is currently fully compatible - with the old interface to avoid confusion and duplication - during the transition parent. SPI and SPI flash must be - enabled together (it is not possible to use driver model - for one and not the other). + Enable driver model for I2C. The I2C uclass interface: probe, read, + write and speed, is implemented with the bus drivers operations, + which provide methods for bus setting and data transfer. Each chip + device (bus child) info is kept as parent platdata. The interface + is defined in include/i2c.h. When i2c bus driver supports the i2c + uclass, but the device drivers not, then DM_I2C_COMPAT config can + be used as compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer"

On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- add i2c uclass description
Changes v3:
- Kconfig: i2c-uclass: fix help message
drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..c83a984 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C. The I2C uclass interface: probe, read,
write and speed, is implemented with the bus drivers operations,
which provide methods for bus setting and data transfer. Each chip
device (bus child) info is kept as parent platdata. The interface
is defined in include/i2c.h. When i2c bus driver supports the i2c
uclass, but the device drivers not, then DM_I2C_COMPAT config can
be used as compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer" -- 1.9.1
Acked-by: Simon Glass sjg@chromium.org

On 31 March 2015 at 21:17, Simon Glass sjg@chromium.org wrote:
On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com Cc: Masahiro Yamada yamada.masahiro@socionext.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- add i2c uclass description
Changes v3:
- Kconfig: i2c-uclass: fix help message
drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 692810d..c83a984 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -2,16 +2,13 @@ config DM_I2C bool "Enable Driver Model for I2C drivers" depends on DM help
Enable driver model for I2C. This SPI flash interface
(spi_flash_probe(), spi_flash_write(), etc.) is then
implemented by the SPI flash uclass. There is one standard
SPI flash driver which knows how to probe most chips
supported by U-Boot. The uclass interface is defined in
include/spi_flash.h, but is currently fully compatible
with the old interface to avoid confusion and duplication
during the transition parent. SPI and SPI flash must be
enabled together (it is not possible to use driver model
for one and not the other).
Enable driver model for I2C. The I2C uclass interface: probe, read,
write and speed, is implemented with the bus drivers operations,
which provide methods for bus setting and data transfer. Each chip
device (bus child) info is kept as parent platdata. The interface
is defined in include/i2c.h. When i2c bus driver supports the i2c
uclass, but the device drivers not, then DM_I2C_COMPAT config can
be used as compatibility layer.
config DM_I2C_COMPAT bool "Enable I2C compatibility layer" -- 1.9.1
Acked-by: Simon Glass sjg@chromium.org
Applied to u-boot-dm/next, thanks!

This commit adds driver model support to software emulated i2c bus driver. This driver supports kernel-style device tree bindings. Fdt properties in use: - compatible - "i2c-gpio" - gpios - data and clock GPIO pin phandles - delay-us - micro seconds delay between GPIOs toggle operations, which is 1/4 of I2C speed clock period.
Added: - Config: CONFIG_DM_I2C_GPIO - File: drivers/i2c/i2c-gpio.c - File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes: - use "i2c-gpio" naming - update comments style - move preprocesor macros into functions - add device tree support - add driver model i2c support - code cleanup, - add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2: - new file for software i2c driver: i2c-gpio.c - update driver naming: use of "i2c-gpio" - add full compatibility with the kernels device-tree "i2c-gpio" node - fix Kconfig entry
Changes V3: - fix Kconfig entry - update file header - remove field "speed" from struct i2c_gpio_bus - move macros to functions - move duplicated routines into functions - update naming with prefix "i2c_gpio_" --- doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 345 ++++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
diff --git a/doc/device-tree-bindings/i2c/i2c-gpio.txt b/doc/device-tree-bindings/i2c/i2c-gpio.txt new file mode 100644 index 0000000..ba56ed5 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-gpio.txt @@ -0,0 +1,37 @@ +I2C gpio device binding +======================= + +Driver: +- drivers/i2c/i2c-gpio.c + +Software i2c device-tree node properties: +Required: +* #address-cells = <1>; +* #size-cells = <0>; +* compatible = "i2c-gpio"; +* gpios = <sda ...>, <scl ...>; + +Optional: +* i2c-gpio,delay-us = <5>; + The resulting transfer speed can be adjusted by setting the delay[us] + between gpio-toggle operations. Speed [Hz] = 1000000 / 4 * udelay[us], + It not defined, then default is 5us (~50KHz). + +Example: + +i2c-gpio@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "i2c-gpio"; + gpios = <&gpd1 0 GPIO_ACTIVE_HIGH>, /* SDA */ + <&gpd1 1 GPIO_ACTIVE_HIGH>; /* CLK */ + + i2c-gpio,delay-us = <5>; + + some_device@5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c83a984..739badc 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -19,6 +19,15 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. It should not be enabled for any board in an official release.
+config DM_I2C_GPIO + bool "Enable Driver Model for software emulated I2C bus driver" + depends on DM_I2C && DM_GPIO + help + Enable the i2c bus driver emulation by using the GPIOs. The bus GPIO + configuration is given by the device tree. Kernel-style device tree + bindings are supported. + Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 774bc94..d9e9f3a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_I2C) += i2c-uclass.o obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o +obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c new file mode 100644 index 0000000..f17839b --- /dev/null +++ b/drivers/i2c/i2c-gpio.c @@ -0,0 +1,345 @@ +/* + * (C) Copyright 2015, Samsung Electronics + * Przemyslaw Marczak p.marczak@samsung.com + * + * This file is based on: drivers/i2c/soft-i2c.c, + * with added driver-model support and code cleanup. + */ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/gpio.h> + +#define DEFAULT_UDELAY 5 +#define RETRIES 0 +#define I2C_ACK 0 +#define I2C_NOACK 1 + +DECLARE_GLOBAL_DATA_PTR; + +enum { + PIN_SDA = 0, + PIN_SCL, + PIN_COUNT, +}; + +struct i2c_gpio_bus { + /** + * udelay - delay [us] between GPIO toggle operations, + * which is 1/4 of I2C speed clock period. + */ + int udelay; + /* sda, scl */ + struct gpio_desc gpios[PIN_COUNT]; +}; + +static int i2c_gpio_sda_get(struct gpio_desc *sda) +{ + return dm_gpio_get_value(sda); +} + +static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit) +{ + if (bit) { + dm_gpio_set_dir_flags(sda, GPIOD_IS_IN); + } else { + dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT); + dm_gpio_set_value(sda, 0); + } +} + +static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit) +{ + dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT); + dm_gpio_set_value(scl, bit); +} + +static void i2c_gpio_write_bit(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, uchar bit) +{ + i2c_gpio_scl_set(scl, 0); + udelay(delay); + i2c_gpio_sda_set(sda, bit); + udelay(delay); + i2c_gpio_scl_set(scl, 1); + udelay(2 * delay); +} + +static int i2c_gpio_read_bit(struct gpio_desc *scl, struct gpio_desc *sda, + int delay) +{ + int value; + + i2c_gpio_scl_set(scl, 1); + udelay(delay); + value = i2c_gpio_sda_get(sda); + udelay(delay); + i2c_gpio_scl_set(scl, 0); + udelay(2 * delay); + + return value; +} + +/* START: High -> Low on SDA while SCL is High */ +static void i2c_gpio_send_start(struct gpio_desc *scl, struct gpio_desc *sda, + int delay) +{ + udelay(delay); + i2c_gpio_sda_set(sda, 1); + udelay(delay); + i2c_gpio_scl_set(scl, 1); + udelay(delay); + i2c_gpio_sda_set(sda, 0); + udelay(delay); +} + +/* STOP: Low -> High on SDA while SCL is High */ +static void i2c_gpio_send_stop(struct gpio_desc *scl, struct gpio_desc *sda, + int delay) +{ + i2c_gpio_scl_set(scl, 0); + udelay(delay); + i2c_gpio_sda_set(sda, 0); + udelay(delay); + i2c_gpio_scl_set(scl, 1); + udelay(delay); + i2c_gpio_sda_set(sda, 1); + udelay(delay); +} + +/* ack should be I2C_ACK or I2C_NOACK */ +static void i2c_gpio_send_ack(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + i2c_gpio_write_bit(scl, sda, delay, ack); + i2c_gpio_scl_set(scl, 0); + udelay(delay); +} + +/** + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * <stop> at the end of the sequence for belts & suspenders. + */ +static void i2c_gpio_send_reset(struct gpio_desc *scl, struct gpio_desc *sda, + int delay) +{ + int j; + + for (j = 0; j < 9; j++) + i2c_gpio_write_bit(scl, sda, delay, 1); + + i2c_gpio_send_stop(scl, sda, delay); +} + +/* Set sda high with low clock, before reading slave data */ +static void i2c_gpio_sda_high(struct gpio_desc *scl, struct gpio_desc *sda, + int delay) +{ + i2c_gpio_scl_set(scl, 0); + udelay(delay); + i2c_gpio_sda_set(sda, 1); + udelay(delay); +} + +/* Send 8 bits and look for an acknowledgement */ +static int i2c_gpio_write_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, uchar data) +{ + int j; + int nack; + + for (j = 0; j < 8; j++) { + i2c_gpio_write_bit(scl, sda, delay, data & 0x80); + data <<= 1; + } + + udelay(delay); + + /* Look for an <ACK>(negative logic) and return it */ + i2c_gpio_sda_high(scl, sda, delay); + nack = i2c_gpio_read_bit(scl, sda, delay); + + return nack; /* not a nack is an ack */ +} + +/** + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar i2c_gpio_read_byte(struct gpio_desc *scl, struct gpio_desc *sda, + int delay, int ack) +{ + int data; + int j; + + i2c_gpio_sda_high(scl, sda, delay); + data = 0; + for (j = 0; j < 8; j++) { + data <<= 1; + data |= i2c_gpio_read_bit(scl, sda, delay); + } + i2c_gpio_send_ack(scl, sda, delay, ack); + + return data; +} + +/* send start and the slave chip address */ +int i2c_send_slave_addr(struct gpio_desc *scl, struct gpio_desc *sda, int delay, + uchar chip) +{ + i2c_gpio_send_start(scl, sda, delay); + + if (i2c_gpio_write_byte(scl, sda, delay, chip)) { + i2c_gpio_send_stop(scl, sda, delay); + return -EIO; + } + + return 0; +} + +static int i2c_gpio_write_data(struct i2c_gpio_bus *bus, uchar chip, + uchar *buffer, int len, + bool end_with_repeated_start) +{ + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + int failures = 0; + + debug("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len); + + if (i2c_send_slave_addr(scl, sda, delay, chip << 1)) { + debug("i2c_write, no chip responded %02X\n", chip); + return -EIO; + } + + while (len-- > 0) { + if (i2c_gpio_write_byte(scl, sda, delay, *buffer++)) + failures++; + } + + if (!end_with_repeated_start) { + i2c_gpio_send_stop(scl, sda, delay); + return failures; + } + + if (i2c_send_slave_addr(scl, sda, delay, (chip << 1) | 0x1)) { + debug("i2c_write, no chip responded %02X\n", chip); + return -EIO; + } + + return failures; +} + +static int i2c_gpio_read_data(struct i2c_gpio_bus *bus, uchar chip, + uchar *buffer, int len) +{ + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + + debug("%s: chip %x buffer: %p len %d\n", __func__, chip, buffer, len); + + while (len-- > 0) + *buffer++ = i2c_gpio_read_byte(scl, sda, delay, len == 0); + + i2c_gpio_send_stop(scl, sda, delay); + + return 0; +} + +static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + int ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + + if (msg->flags & I2C_M_RD) + ret = i2c_gpio_read_data(bus, msg->addr, msg->buf, + msg->len); + else + ret = i2c_gpio_write_data(bus, msg->addr, msg->buf, + msg->len, next_is_read); + + if (ret) + return -EREMOTEIO; + } + + return 0; +} + +static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + unsigned int delay = bus->udelay; + int ret; + + i2c_gpio_send_start(scl, sda, delay); + ret = i2c_gpio_write_byte(scl, sda, delay, (chip << 1) | 0); + i2c_gpio_send_stop(scl, sda, delay); + + debug("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n", + __func__, dev->seq, dev->name, chip, chip_flags, ret); + + return ret; +} + +static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + struct gpio_desc *scl = &bus->gpios[PIN_SCL]; + struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + + bus->udelay = 1000000 / (speed_hz << 2); + + i2c_gpio_send_reset(scl, sda, bus->udelay); + + return 0; +} + +static int i2c_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct i2c_gpio_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + int ret; + + ret = gpio_request_list_by_name(dev, "gpios", bus->gpios, + ARRAY_SIZE(bus->gpios), 0); + if (ret < 0) + goto error; + + bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us", + DEFAULT_UDELAY); + + return 0; +error: + error("Can't get %s gpios! Error: %d", dev->name, ret); + return ret; +} + +static const struct dm_i2c_ops i2c_gpio_ops = { + .xfer = i2c_gpio_xfer, + .probe_chip = i2c_gpio_probe, + .set_bus_speed = i2c_gpio_set_bus_speed, +}; + +static const struct udevice_id i2c_gpio_ids[] = { + { .compatible = "i2c-gpio" }, + { } +}; + +U_BOOT_DRIVER(i2c_gpio) = { + .name = "i2c-gpio", + .id = UCLASS_I2C, + .of_match = i2c_gpio_ids, + .ofdata_to_platdata = i2c_gpio_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct i2c_gpio_bus), + .ops = &i2c_gpio_ops, +};

On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
This commit adds driver model support to software emulated i2c bus driver. This driver supports kernel-style device tree bindings. Fdt properties in use:
- compatible - "i2c-gpio"
- gpios - data and clock GPIO pin phandles
- delay-us - micro seconds delay between GPIOs toggle operations, which is 1/4 of I2C speed clock period.
Added:
- Config: CONFIG_DM_I2C_GPIO
- File: drivers/i2c/i2c-gpio.c
- File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes:
- use "i2c-gpio" naming
- update comments style
- move preprocesor macros into functions
- add device tree support
- add driver model i2c support
- code cleanup,
- add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- new file for software i2c driver: i2c-gpio.c
- update driver naming: use of "i2c-gpio"
- add full compatibility with the kernels device-tree "i2c-gpio" node
- fix Kconfig entry
Changes V3:
- fix Kconfig entry
- update file header
- remove field "speed" from struct i2c_gpio_bus
- move macros to functions
- move duplicated routines into functions
- update naming with prefix "i2c_gpio_"
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 345 ++++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
Acked-by: Simon Glass sjg@chromium.org

On 31 March 2015 at 21:18, Simon Glass sjg@chromium.org wrote:
On 31 March 2015 at 10:57, Przemyslaw Marczak p.marczak@samsung.com wrote:
This commit adds driver model support to software emulated i2c bus driver. This driver supports kernel-style device tree bindings. Fdt properties in use:
- compatible - "i2c-gpio"
- gpios - data and clock GPIO pin phandles
- delay-us - micro seconds delay between GPIOs toggle operations, which is 1/4 of I2C speed clock period.
Added:
- Config: CONFIG_DM_I2C_GPIO
- File: drivers/i2c/i2c-gpio.c
- File: doc/device-tree-bindings/i2c/i2c-gpio.txt
Driver base code is taken from: drivers/i2c/soft-i2c.c, changes:
- use "i2c-gpio" naming
- update comments style
- move preprocesor macros into functions
- add device tree support
- add driver model i2c support
- code cleanup,
- add Kconfig entry
Signed-off-by: Przemyslaw Marczak p.marczak@samsung.com CC: Masahiro Yamada yamada.masahiro@socionext.com Cc: Lukasz Majewski l.majewski@samsung.com Cc: Mike Frysinger vapier@gentoo.org Cc: Simon Glass sjg@chromium.org Cc: Heiko Schocher hs@denx.de
Changes V2:
- new file for software i2c driver: i2c-gpio.c
- update driver naming: use of "i2c-gpio"
- add full compatibility with the kernels device-tree "i2c-gpio" node
- fix Kconfig entry
Changes V3:
- fix Kconfig entry
- update file header
- remove field "speed" from struct i2c_gpio_bus
- move macros to functions
- move duplicated routines into functions
- update naming with prefix "i2c_gpio_"
doc/device-tree-bindings/i2c/i2c-gpio.txt | 37 ++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-gpio.c | 345 ++++++++++++++++++++++++++++++ 4 files changed, 392 insertions(+) create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt create mode 100644 drivers/i2c/i2c-gpio.c
Acked-by: Simon Glass sjg@chromium.org
Applied to u-boot-dm/next, thanks!
participants (5)
-
Heiko Schocher
-
Lukasz Majewski
-
Masahiro Yamada
-
Przemyslaw Marczak
-
Simon Glass