From mboxrd@z Thu Jan 1 00:00:00 1970 From: Heiko Schocher Date: Tue, 24 Mar 2015 07:01:33 +0100 Subject: [U-Boot] [PATCH 1/3] dm: i2c soft: enable driver model for software i2c driver In-Reply-To: References: <1425983444-18565-1-git-send-email-p.marczak@samsung.com> <1425983444-18565-2-git-send-email-p.marczak@samsung.com> Message-ID: <5510FDBD.2020109@denx.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hello Simon, Przemyslaw, Am 24.03.2015 00:38, schrieb Simon Glass: > Hi Przemyslaw, > > On 10 March 2015 at 04:30, Przemyslaw Marczak 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 at 13500000"; >> i2c6 = "/soft-i2c at 1"; >> [...] >> }; >> >> /* Define the basic bus attributes */ >> soft-i2c at 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 at 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 >> Cc: Masahiro Yamada >> Cc: Mike Frysinger >> Cc: Simon Glass >> Cc: Heiko Schocher 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 >> + * >> * (C) Copyright 2009 >> * Heiko Schocher, DENX Software Engineering, hs at denx.de. >> * Changes for multibus/multiadapter I2C support. >> @@ -14,6 +17,11 @@ >> */ >> >> #include >> +#include >> +#include >> +#include >> +#include >> +#include >> #ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ >> #include >> #include >> @@ -32,11 +40,9 @@ >> #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) >> #include >> #endif >> -#include >> >> +#if defined(CONFIG_SYS_I2C) >> #if defined(CONFIG_SOFT_I2C_GPIO_SCL) >> -# include >> - >> # 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 >> * 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 (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 > -- DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany