dm: i2c: enable driver model for software i2c
cover 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(-)
Signature · 1 line
1.9.1
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
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.
--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
drivers/i2c/Makefile +1 -0
@@ -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
drivers/i2c/soft_i2c.c +399 -11
567 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
Hi Przemyslaw,
Quoted 660 lines
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.
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410+++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
drivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/soft_i2c.c +8
-0@@ -1,4 +1,7 @@/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs at 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>drivers/i2c/soft_i2c.c +60
-3@@ -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_NOACK1 /*PD_SDA
level to noack a byte */drivers/i2c/soft_i2c.c +13
-2-#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); #endifdrivers/i2c/soft_i2c.c +0
-6-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. */drivers/i2c/soft_i2c.c +152
-0+#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)
#endifdrivers/i2c/soft_i2c.c +12
-0+#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) +{drivers/i2c/soft_i2c.c +6
-0+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); +
drivers/i2c/soft_i2c.c +25
-0+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); +
drivers/i2c/soft_i2c.c +2
-0+send_start (scl, sda, delay); +write_byte (scl, sda, delay, (chip <<1 ) |1 );/* read cycle */ +
drivers/i2c/soft_i2c.c +9
-0+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) +{
drivers/i2c/soft_i2c.c +5
-0+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);drivers/i2c/soft_i2c.c +2
-0+if (msg->flags &I2C_M_RD ) { + ret = i2c_read_data(i2c_bus, msg->addr,
msg->buf,drivers/i2c/soft_i2c.c +3
-0+ msg->len); + }else { + ret = i2c_write_data(i2c_bus, msg->addr,
msg->buf,drivers/i2c/soft_i2c.c +10
-0+ msg->len, next_is_read); + } +if (ret) +return -EREMOTEIO ; + } + +return 0 ; +} + +static int soft_i2c_probe(struct udevice *dev, uint chip, uint
chip_flags) +{drivers/i2c/soft_i2c.c +17
-0+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 intspeed) +{
drivers/i2c/soft_i2c.c +10
-0+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: %luus)\n",
drivers/i2c/soft_i2c.c +14
-0+ __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,drivers/i2c/soft_i2c.c +1
-0+ 0, &i2c_bus->scl,
GPIOD_IS_OUT);drivers/i2c/soft_i2c.c +5
-0+if (ret) + goto error; + + pin_name ="data-pin" ; + ret = gpio_request_by_name_nodev(blob, dev->of_offset,
pin_name,drivers/i2c/soft_i2c.c +1
-0+ 0, &i2c_bus->sda,
GPIOD_IS_OUT);drivers/i2c/soft_i2c.c +4
-0+if (ret) + goto error; + + PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq,
dev->name); +drivers/i2c/soft_i2c.c +3
-0+return 0 ; +error: + error("Can't get %s for soft i2c dev: %s", pin_name,
dev->name);drivers/i2c/soft_i2c.c +13
-0+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" }, + { } +};
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 863
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-0+ +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 */
Signature · 5 lines
Best regards, Lukasz Majewski Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
In reply to Przemyslaw MarczakHi Przemyslaw,
Quoted 40 lines · Przemyslaw Marczak
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!)
Quoted 7 lines
...
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?
Quoted 196 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)drivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/soft_i2c.c +97
-11@@ -1,4 +1,7 @@/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs at 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);
drivers/i2c/soft_i2c.c static void send_reset(void) · line 264
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-0+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?
Quoted 261 lines
drivers/i2c/soft_i2c.c +147
-0+} +#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;
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 728
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-0+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
Quoted 86 lines
drivers/i2c/soft_i2c.c +86
-0+ } + +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 ;
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 824
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-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
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 826
Quoted 2 lines
drivers/i2c/soft_i2c.c +2
-0+ 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.
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 830
Quoted 4 lines
drivers/i2c/soft_i2c.c +4
-0+if (ret) + goto error; + + pin_name ="data-pin" ;
nit: or here
Quoted 34 lines
drivers/i2c/soft_i2c.c +33
-1+ 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 */ --
Quoted 2 lines
1.9.1
Regards,
Simon
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
Hello Simon, Przemyslaw,
Am 24.03.2015 00:38, schrieb Simon Glass:
Quoted 45 lines
Hi Przemyslaw,
Quoted 42 lines · Przemyslaw Marczak
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!)...
Quoted 7 lines
Quoted 5 lines
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?
Quoted 5 lines
Quoted 5 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>
Hups... seems I missed this patches ...
Quoted 4 lines
Quoted 4 lines
--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)
Thanks for this work!
Quoted 190 lines
Quoted 190 lines
drivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/soft_i2c.c +102
-11@@ -1,4 +1,7 @@/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs at 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 );
Quoted 8 lines
drivers/i2c/soft_i2c.c static void send_reset(void) · line 264
Quoted 5 lines
drivers/i2c/soft_i2c.c +5
-0+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
Quoted 425 lines
Quoted 261 lines
drivers/i2c/soft_i2c.c +147
-0+} +#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;drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 728
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-0+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 ;
-EIOI thinkQuoted 86 lines
drivers/i2c/soft_i2c.c +86
-0+ } + +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 ;drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 824
Quoted 10 lines
drivers/i2c/soft_i2c.c +10
-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
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 826
Quoted 2 lines
drivers/i2c/soft_i2c.c +2
-0+ 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.
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 830
Quoted 4 lines
drivers/i2c/soft_i2c.c +4
-0+if (ret) + goto error; + + pin_name ="data-pin" ;nit: or here
Quoted 34 lines
drivers/i2c/soft_i2c.c +33
-1+ 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 */ --Quoted 2 lines
1.9.1
Regards,
Simon
Signature · 2 lines
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
Hello,
Quoted 54 lines · Heiko Schocher
Hello Simon, Przemyslaw,
Am 24.03.2015 00:38, schrieb Simon Glass:
Quoted 51 lines
Hi Przemyslaw,
Quoted 47 lines · Przemyslaw Marczak
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.
Quoted 8 lines
Quoted 4 lines
Quoted 1 line
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.
Quoted 232 lines
Quoted 5 lines
Quoted 5 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>Hups... seems I missed this patches ...
Quoted 5 lines
Quoted 5 lines
--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)Thanks for this work!
Quoted 202 lines
Quoted 202 lines
drivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/soft_i2c.c +69
-3@@ -1,4 +1,7 @@/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs at 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
*/drivers/i2c/soft_i2c.c +0
-0@@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR;#define I2C_ACK 0/* PD_SDA level to ack a byte */
#defineI2C_NOACK1 /*PD_SDAlevel to noack a
byte */drivers/i2c/soft_i2c.c +15
-8-#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. */drivers/i2c/soft_i2c.c +18
-0+#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 );Quoted 8 lines
drivers/i2c/soft_i2c.c static void send_reset(void) · line 264
Quoted 5 lines
drivers/i2c/soft_i2c.c +5
-0+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 ...
Quoted 5 lines
@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,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver
In reply to Simon GlassHello Simon,
Quoted 45 lines · Simon Glass
Hi Przemyslaw,
Quoted 42 lines · Przemyslaw Marczak
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!)...
Quoted 8 lines
Quoted 5 lines
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.
Quoted 201 lines
Quoted 201 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Makefile | 1 + drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 400 insertions(+), 11 deletions(-)drivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/soft_i2c.c +102
-11@@ -1,4 +1,7 @@/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* * (C) Copyright 2009 * Heiko Schocher, DENX Software Engineering, hs at 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 );
Quoted 9 lines
drivers/i2c/soft_i2c.c static void send_reset(void) · line 264
Quoted 5 lines
drivers/i2c/soft_i2c.c +5
-0+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.
Quoted 265 lines
Quoted 265 lines
drivers/i2c/soft_i2c.c +151
-0+} +#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);
Quoted 9 lines
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 728
Quoted 6 lines
drivers/i2c/soft_i2c.c +6
-0+ +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 ;
-EIOI think
Ok, will fix.
Quoted 90 lines
Quoted 90 lines
drivers/i2c/soft_i2c.c +90
-0+ } + +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) +{
Quoted 9 lines
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 824
Quoted 6 lines
drivers/i2c/soft_i2c.c +6
-0+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.
Quoted 5 lines
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 826
Quoted 2 lines
drivers/i2c/soft_i2c.c +2
-0+ 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.
Quoted 38 lines
drivers/i2c/soft_i2c.c U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, · line 830
Quoted 4 lines
drivers/i2c/soft_i2c.c +4
-0+if (ret) + goto error; + + pin_name ="data-pin" ;nit: or here
Quoted 31 lines
drivers/i2c/soft_i2c.c +31
-0+ 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,
Quoted 9 lines
Quoted 5 lines
drivers/i2c/soft_i2c.c +2
-1+}; +#endif /* CONFIG_DM_I2C_SOFT */ --1.9.1
Regards,
Simon
Best regards,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c
In reply to Przemyslaw Marczak--- drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
drivers/i2c/Kconfig +1 -10
@@ -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"
Signature · 1 line
1.9.1
PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c
Hi Przemyslaw,
Quoted 21 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)drivers/i2c/Kconfig +0
-4@@ -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
drivers/i2c/Kconfig config DM_I2C · line 8
Quoted 10 lines
drivers/i2c/Kconfig +1
-6- 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"
Signature · 5 lines
Best regards, Lukasz Majewski Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c
In reply to Przemyslaw MarczakHi,
Quoted 18 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)drivers/i2c/Kconfig +0
-1@@ -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
drivers/i2c/Kconfig config DM_I2C · line 5
Quoted 10 lines
drivers/i2c/Kconfig +1
-9- (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.
Quoted 6 lines
drivers/i2c/Kconfig +0
-1config DM_I2C_COMPAT bool "Enable I2C compatibility layer" --1.9.1
Regards,
Simon
PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c
Hello Simon,
Quoted 34 lines · Simon Glass
Hi,
Quoted 18 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Kconfig | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)drivers/i2c/Kconfig +0
-1@@ -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 interfacedrivers/i2c/Kconfig config DM_I2C · line 5
Quoted 10 lines
drivers/i2c/Kconfig +1
-9- (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 :-)
Quoted 5 lines
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 :)
Quoted 10 lines
Quoted 6 lines
drivers/i2c/Kconfig +0
-1config DM_I2C_COMPAT bool "Enable I2C compatibility layer" --1.9.1
Regards,
Simon
Best regards,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c
In reply to Przemyslaw Marczak--- drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
drivers/i2c/Kconfig +43 -0
@@ -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 at 13500000"; + i2c6 = "/soft-i2c at 1"; + [...] + } + + /* And next 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!) + ... + + 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
Signature · 1 line
1.9.1
PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c
Hi Przemyslaw,
Quoted 61 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)drivers/i2c/Kconfig +0
-0@@ -13,6 +13,49 @@ config DM_I2C_COMPAT to convert all code for a board in a single commit. Itshould not be enabled for any board in an official release.
drivers/i2c/Kconfig +6
-0+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, likebelow. +
drivers/i2c/Kconfig +1
-0+ /* First, define the alias number to have continuous busnumbering */
drivers/i2c/Kconfig +29
-0+ aliases { + [...] + i2c5 = "/i2c at 13500000"; + i2c6 = "/soft-i2c at 1"; + [...] + } + + /* And next 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 ataddress
0x0)drivers/i2c/Kconfig +4
-0+ # Valid chip addresses: 0x44 (success!) + ... + + Driving the bus lines is done by dm gpio calls in thepreprocessor
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 61
Quoted 5 lines
drivers/i2c/Kconfig +2
-0+ macros. Each, can be redefined by the user. + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2C
Signature · 5 lines
Best regards, Lukasz Majewski Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c
In reply to Przemyslaw MarczakQuoted 9 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>--- drivers/i2c/Kconfig | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
This is very nice.
PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c
In reply to Przemyslaw MarczakHi.
2015-03-10 19:30 GMT+09:00 Przemyslaw Marczak <[email protected]>:
Quoted 2 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>
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 at socionext.com
Quoted 6 lines
drivers/i2c/Kconfig +0
-0
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 20
Quoted 8 lines
drivers/i2c/Kconfig +5
-0@@ -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!
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 29
Quoted 9 lines
drivers/i2c/Kconfig +9
-0+ The bus configuration is given by the device-tree, like below. + + /* First, define the alias number to have continuous bus numbering */ + aliases { + [...] + i2c5 = "/i2c at 13500000"; + i2c6 = "/soft-i2c at 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?
Quoted 7 lines
drivers/i2c/Kconfig +7
-0+ /* And next 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 */
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 47
Quoted 10 lines
drivers/i2c/Kconfig +10
-0+ 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>; + [...] + }; + }
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/
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 54
Quoted 6 lines
drivers/i2c/Kconfig +6
-0+ 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.
Quoted 13 lines
drivers/i2c/Kconfig +3
-1+ 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 at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
Signature · 2 lines
Best Regards Masahiro Yamada
PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c
Hello Masahiro,
Quoted 4 lines · Masahiro Yamada
Hi.
Quoted 11 lines
2015-03-10 19:30 GMT+09:00 Przemyslaw Marczak <[email protected]>:
Quoted 2 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>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 at socionext.com
Ok, will update this.
Quoted 27 lines
Quoted 6 lines
drivers/i2c/Kconfig +0
-0drivers/i2c/Kconfig config DM_I2C_COMPAT · line 20
Quoted 8 lines
drivers/i2c/Kconfig +5
-0@@ -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!
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 29
Quoted 9 lines
drivers/i2c/Kconfig +9
-0+ The bus configuration is given by the device-tree, like below. + + /* First, define the alias number to have continuous bus numbering */ + aliases { + [...] + i2c5 = "/i2c at 13500000"; + i2c6 = "/soft-i2c at 1"; + [...] + }
Quoted 8 lines
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.
Quoted 18 lines
Quoted 7 lines
drivers/i2c/Kconfig +7
-0+ /* And next 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 */drivers/i2c/Kconfig config DM_I2C_COMPAT · line 47
Quoted 10 lines
drivers/i2c/Kconfig +10
-0+ 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>; + [...] + }; + }
Quoted 7 lines
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.
Quoted 10 lines
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 54
Quoted 8 lines
drivers/i2c/Kconfig +6
-0+ 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!) + ...
Quoted 3 lines
This is the usage of "i2c" command.
It is not specific to this option, either.
Ok.
Quoted 12 lines
Quoted 12 lines
drivers/i2c/Kconfig +3
-1+ 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
Quoted 7 lines
Quoted 4 lines
_______________________________________________
U-Boot mailing list
U-Boot at lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot
Thanks for review!
Best regards,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
v2 cover dm: i2c: enable driver model for i2c gpio
In reply to Przemyslaw MarczakThis 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
Signature · 1 line
1.9.1
PATCH v2 1/3 dm: gpio: request list: return the count if requests max_count reached
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.
--- drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
drivers/gpio/gpio-uclass.c +1 -5
@@ -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 )
Signature · 1 line
1.9.1
PATCH v2 1/3 dm: gpio: request list: return the count if requests max_count reached
Quoted 7 lines · Przemyslaw Marczak
The function
gpio_request_list_by_name_nodev()returned-ENOSPCerror, 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 requestedmax_count.
Quoted 5 lines
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Simon Glass <[email protected]>--- drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
PATCH v2 2/3 Kconfig: i2c: fix help message related to dm i2c
In reply to Przemyslaw MarczakChanges V2:
- add i2c uclass description
--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/i2c/Kconfig +7 -10
@@ -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"
Signature · 1 line
1.9.1
PATCH v2 2/3 Kconfig: i2c: fix help message related to dm i2c
Hi Przemyslaw,
Quoted 11 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>Changes V2:
- add i2c uclass description--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
(see nit below)
Quoted 12 lines
drivers/i2c/Kconfig +0
-3@@ -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
drivers/i2c/Kconfig config DM_I2C · line 7
Quoted 10 lines
drivers/i2c/Kconfig +3
-7- 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
Quoted 10 lines
drivers/i2c/Kconfig +4
-1+ 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
PATCH v2 2/3 Kconfig: i2c: fix help message related to dm i2c
Hello Simon,
Quoted 44 lines · Simon Glass
Hi Przemyslaw,
Quoted 11 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>Changes V2:
- add i2c uclass description--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)Acked-by Simon Glass <[email protected]>(see nit below)
Quoted 12 lines
drivers/i2c/Kconfig +0
-3@@ -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 standarddrivers/i2c/Kconfig config DM_I2C · line 7
Quoted 10 lines
drivers/i2c/Kconfig +3
-7- 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 chipCan we tweak this a bit?
Quoted 4 lines
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.
Quoted 5 lines
Quoted 5 lines
drivers/i2c/Kconfig +4
-0+ 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.
Quoted 9 lines
Quoted 5 lines
drivers/i2c/Kconfig +0
-1config DM_I2C_COMPAT bool "Enable I2C compatibility layer" --1.9.1
Regards,
Simon
Best regards,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
PATCH v2 3/3 dm: i2c: add i2c-gpio driver
In reply to Przemyslaw MarczakThis 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
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
doc/device-tree-bindings/i2c/i2c-gpio.txt new file +37 -0
@@ -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 at 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 at 5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +};
drivers/i2c/Kconfig +9 -0
@@ -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
drivers/i2c/Makefile +1 -0
@@ -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
drivers/i2c/i2c-gpio.c new file +353 -0
353 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v2 3/3 dm: i2c: add i2c-gpio driver
Hi Przemyslaw,
Quoted 23 lines · Przemyslaw Marczak
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.txtDriver 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.
- addKconfigentrySigned-off-by Przemyslaw Marczak <[email protected]>CC Masahiro Yamada <[email protected]>Cc Lukasz Majewski <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>
Quoted 5 lines
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
- fixKconfigentry
Sorry I still have a few more comments.
Quoted 57 lines
--- 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.cdoc/device-tree-bindings/i2c/i2c-gpio.txt new file +37
-0@@ -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 at 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 at 5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +};drivers/i2c/Kconfig +0
-0
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 26
Quoted 8 lines
drivers/i2c/Kconfig +5
-0@@ -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.
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 27
Quoted 1 line
drivers/i2c/Kconfig +1
-0+ The bus gpio configuration is given by the device-tree.
GPIO
device tree
(no hypen)
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 28
Quoted 1 line
drivers/i2c/Kconfig +1
-0+ Kernel style device-tree node for i2c-gpio is supported.
Kernel-style device tree bindings are supported
Quoted 50 lines
drivers/i2c/Kconfig +2
-0+ Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2Cdrivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/i2c-gpio.c new file +27
-0@@ -0,0 +1,353 @@ +/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* Add driver-model support as a separated driver file +* +* (C) Copyright 2009 +* Heiko Schocher, DENX Software Engineering, hs at denx.de. +* Changes for multibus/multiadapter I2C support. +* +* (C) Copyright 2001, 2002 +* Wolfgang Denk, DENX Software Engineering, wd at denx.de. +* +* SPDX-License-Identifier: GPL-2.0+ +* +* This has been changed substantially by Gerald Van Baren, Custom IDEAS, +* vanbaren at 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
drivers/i2c/i2c-gpio.c line 37
Quoted 10 lines
drivers/i2c/i2c-gpio.c new file +10
-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?
drivers/i2c/i2c-gpio.c line 43
Quoted 6 lines
drivers/i2c/i2c-gpio.c new file +6
-0+ +DECLARE_GLOBAL_DATA_PTR ; + +enum { +PIN_SDA =0 , +PIN_SCL ,
PIN_COUNT
drivers/i2c/i2c-gpio.c line 45
Quoted 2 lines
drivers/i2c/i2c-gpio.c new file +2
-0+}; +
Document these members - speed is in HzHz, udelay is the delay for what?
drivers/i2c/i2c-gpio.c line 49
Quoted 4 lines
drivers/i2c/i2c-gpio.c new file +4
-0+struct i2c_gpio_bus { +unsigned int speed; +unsigned long udelay; +struct gpio_desc gpios[2 ];/* sda, scl */
gpios[PIN_COUNT]
drivers/i2c/i2c-gpio.c line 58
Quoted 9 lines
drivers/i2c/i2c-gpio.c new file +9
-0+}; + +/* 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?
drivers/i2c/i2c-gpio.c line 60
Quoted 2 lines
drivers/i2c/i2c-gpio.c new file +2
-0+ +static int I2C_READ (struct gpio_desc *sda)
Lower case - how about gpio_i2c_read()? Same for others.
drivers/i2c/i2c-gpio.c line 68
Quoted 8 lines
drivers/i2c/i2c-gpio.c new file +8
-0+{ +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.
Quoted 26 lines
drivers/i2c/i2c-gpio.c new file +26
-0+ }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; +
drivers/i2c/i2c-gpio.c line 104
Quoted 10 lines
drivers/i2c/i2c-gpio.c new file +10
-0+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?
Quoted 49 lines
drivers/i2c/i2c-gpio.c new file +49
-0+ } +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;
drivers/i2c/i2c-gpio.c line 163
Quoted 10 lines
drivers/i2c/i2c-gpio.c new file +10
-0+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?
Quoted 129 lines
drivers/i2c/i2c-gpio.c new file +129
-0+ + 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;
drivers/i2c/i2c-gpio.c line 302
Quoted 10 lines
drivers/i2c/i2c-gpio.c new file +10
-0+} + +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?
Quoted 52 lines
drivers/i2c/i2c-gpio.c new file +51
-1+ +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, +}; --
Quoted 2 lines
1.9.1
Regards,
Simon
PATCH v2 3/3 dm: i2c: add i2c-gpio driver
Hello Simon,
Quoted 26 lines · Simon Glass
Hi Przemyslaw,
Quoted 23 lines · Przemyslaw Marczak
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.txtDriver 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.
- addKconfigentrySigned-off-by Przemyslaw Marczak <[email protected]>CC Masahiro Yamada <[email protected]>Cc Lukasz Majewski <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>
Quoted 8 lines
Quoted 5 lines
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
- fixKconfigentrySorry I still have a few more comments.
OK, this is the purpose of this list:)
Quoted 70 lines
Quoted 57 lines
--- 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.txtcreate mode 100644drivers/i2c/i2c-gpio.cdoc/device-tree-bindings/i2c/i2c-gpio.txt new file +37
-0@@ -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 at 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 at 5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +};drivers/i2c/Kconfig +0
-0drivers/i2c/Kconfig config DM_I2C_COMPAT · line 26
Quoted 8 lines
drivers/i2c/Kconfig +5
-0@@ -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.
drivers/i2c/Kconfig config DM_I2C_COMPAT · line 27
Quoted 1 line
drivers/i2c/Kconfig +1
-0+ The bus gpio configuration is given by the device-tree.
Quoted 9 lines
GPIO
device tree
(no hypen)drivers/i2c/Kconfig config DM_I2C_COMPAT · line 28
Quoted 1 line
drivers/i2c/Kconfig +1
-0+ Kernel style device-tree node for i2c-gpio is supported.Kernel-style device tree bindings are supported
Thanks, will fix the all above.
Quoted 54 lines
Quoted 54 lines
drivers/i2c/Kconfig +2
-0+ Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt + config SYS_I2C_UNIPHIER bool "UniPhier I2C driver" depends on ARCH_UNIPHIER && DM_I2Cdrivers/i2c/Makefile +1
-0@@ -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.odrivers/i2c/i2c-gpio.c new file +31
-0@@ -0,0 +1,353 @@ +/* +* (C) Copyright 2015, Samsung Electronics +* Przemyslaw Marczak <[email protected]> +* Add driver-model support as a separated driver file +* +* (C) Copyright 2009 +* Heiko Schocher, DENX Software Engineering, hs at denx.de. +* Changes for multibus/multiadapter I2C support. +* +* (C) Copyright 2001, 2002 +* Wolfgang Denk, DENX Software Engineering, wd at denx.de. +* +* SPDX-License-Identifier: GPL-2.0+ +* +* This has been changed substantially by Gerald Van Baren, Custom IDEAS, +* vanbaren at 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
Quoted 9 lines
drivers/i2c/i2c-gpio.c line 37
Quoted 6 lines
drivers/i2c/i2c-gpio.c new file +6
-0+#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().
Quoted 9 lines
drivers/i2c/i2c-gpio.c line 43
Quoted 6 lines
drivers/i2c/i2c-gpio.c new file +6
-0+ +DECLARE_GLOBAL_DATA_PTR ; + +enum { +PIN_SDA =0 , +PIN_SCL ,
PIN_COUNT
Ok
Quoted 5 lines
drivers/i2c/i2c-gpio.c line 45
Quoted 2 lines
drivers/i2c/i2c-gpio.c new file +2
-0+}; +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.
Quoted 7 lines
drivers/i2c/i2c-gpio.c line 49
Quoted 4 lines
drivers/i2c/i2c-gpio.c new file +4
-0+struct i2c_gpio_bus { +unsigned int speed; +unsigned long udelay; +struct gpio_desc gpios[2 ];/* sda, scl */ gpios[PIN_COUNT]
Ok.
Quoted 12 lines
drivers/i2c/i2c-gpio.c line 58
Quoted 9 lines
drivers/i2c/i2c-gpio.c new file +9
-0+}; + +/* 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?
Quoted 5 lines
drivers/i2c/i2c-gpio.c line 60
Quoted 2 lines
drivers/i2c/i2c-gpio.c new file +2
-0+ +static int I2C_READ (struct gpio_desc *sda)Lower case - how about
gpio_i2c_read()? Same for others.
Ok, will clean this functions.
Quoted 11 lines
drivers/i2c/i2c-gpio.c line 68
Quoted 8 lines
drivers/i2c/i2c-gpio.c new file +8
-0+{ +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.
Quoted 30 lines
Quoted 30 lines
drivers/i2c/i2c-gpio.c new file +30
-0+ }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++) {
Quoted 9 lines
drivers/i2c/i2c-gpio.c line 104
Quoted 6 lines
drivers/i2c/i2c-gpio.c new file +6
-0+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.
Quoted 54 lines
Quoted 54 lines
drivers/i2c/i2c-gpio.c new file +54
-0+ } +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);
Quoted 9 lines
drivers/i2c/i2c-gpio.c line 163
Quoted 5 lines
drivers/i2c/i2c-gpio.c new file +5
-0+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.
Quoted 133 lines
Quoted 133 lines
drivers/i2c/i2c-gpio.c new file +133
-0+ + 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) +{
Quoted 9 lines
drivers/i2c/i2c-gpio.c line 302
Quoted 6 lines
drivers/i2c/i2c-gpio.c new file +6
-0+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.
Quoted 49 lines
Quoted 49 lines
drivers/i2c/i2c-gpio.c new file +49
-0+ +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 ),
Quoted 9 lines
Quoted 5 lines
drivers/i2c/i2c-gpio.c new file +2
-1+ .ops = &i2c_gpio_ops, +}; --1.9.1
Regards,
Simon
Thank you for the review.
Best regards,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
v3 cover dm: i2c: enable driver model for i2c gpio
In reply to Przemyslaw MarczakThis 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
Signature · 1 line
1.9.1
PATCH v3 1/3 dm: gpio: request list: return the count if requests max_count reached
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.
Changes V3:
- new commit
--- drivers/gpio/gpio-uclass.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-)
drivers/gpio/gpio-uclass.c +1 -5
@@ -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 )
Signature · 1 line
1.9.1
PATCH v3 1/3 dm: gpio: request list: return the count if requests max_count reached
Quoted 11 lines · Przemyslaw Marczak
The function
gpio_request_list_by_name_nodev()returned-ENOSPCerror, 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 requestedmax_count.Signed-off-by Przemyslaw Marczak <[email protected]>Cc Simon Glass <[email protected]>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?
PATCH v3 1/3 dm: gpio: request list: return the count if requests max_count reached
Quoted 10 lines · Simon Glass
Quoted 9 lines · Przemyslaw Marczak
The function
gpio_request_list_by_name_nodev()returned-ENOSPCerror, 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 requestedmax_count.Signed-off-by Przemyslaw Marczak <[email protected]>Cc Simon Glass <[email protected]>
Quoted 7 lines
Quoted 2 lines
Changes V3:
- new commitThere'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 <[email protected]>
Applied to u-boot-dm/next, thanks!
PATCH v3 1/3 dm: gpio: request list: return the count if requests max_count reached
In reply to Simon GlassHello Simon,
Quoted 10 lines · Simon Glass
Quoted 9 lines · Przemyslaw Marczak
The function
gpio_request_list_by_name_nodev()returned-ENOSPCerror, 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 requestedmax_count.Signed-off-by Przemyslaw Marczak <[email protected]>Cc Simon Glass <[email protected]>
Quoted 8 lines
Quoted 2 lines
Changes V3:
- new commitThere'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 <[email protected]>
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,
Signature · 4 lines
Przemyslaw Marczak Samsung R&D Institute Poland Samsung Electronics p.marczak at samsung.com
PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c
In reply to Przemyslaw MarczakChanges V2:
- add i2c uclass description
Changes v3:
- Kconfig: i2c-uclass: fix help message
--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/i2c/Kconfig +7 -10
@@ -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"
Signature · 1 line
1.9.1
PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c
Quoted 44 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>Changes V2:
- add i2c uclass descriptionChanges v3:
-Kconfig: i2c-uclass: fix help message--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)drivers/i2c/Kconfig +7
-11@@ -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" --
Quoted 2 lines
1.9.1
PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c
Quoted 42 lines · Simon Glass
Quoted 41 lines · Przemyslaw Marczak
Signed-off-by Przemyslaw Marczak <[email protected]>Cc Masahiro Yamada <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>Changes V2:
- add i2c uclass descriptionChanges v3:
-Kconfig: i2c-uclass: fix help message--- drivers/i2c/Kconfig | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-)drivers/i2c/Kconfig +7
-10@@ -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.
Quoted 7 lines
Quoted 5 lines
drivers/i2c/Kconfig +0
-1config DM_I2C_COMPAT bool "Enable I2C compatibility layer" --1.9.1
Acked-by Simon Glass <[email protected]>
Applied to u-boot-dm/next, thanks!
PATCH v3 3/3 dm: i2c: add i2c-gpio driver
In reply to Przemyslaw MarczakThis 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
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
doc/device-tree-bindings/i2c/i2c-gpio.txt new file +37 -0
@@ -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 at 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 at 5 { + compatible = "some_device"; + reg = <0x5>; + ... + }; +};
drivers/i2c/Kconfig +9 -0
@@ -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
drivers/i2c/Makefile +1 -0
@@ -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
drivers/i2c/i2c-gpio.c new file +345 -0
345 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v3 3/3 dm: i2c: add i2c-gpio driver
Quoted 41 lines · Przemyslaw Marczak
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.txtDriver 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,
- addKconfigentrySigned-off-by Przemyslaw Marczak <[email protected]>CC Masahiro Yamada <[email protected]>Cc Lukasz Majewski <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>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
- fixKconfigentryChanges V3:
- fixKconfigentry
- update file header
- remove field "speed" from structi2c_gpio_bus
- move macros to functions
- move duplicated routines into functions
- update naming with prefix "i2c_gpio_"
Quoted 8 lines
--- 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
PATCH v3 3/3 dm: i2c: add i2c-gpio driver
Quoted 51 lines · Simon Glass
Quoted 41 lines · Przemyslaw Marczak
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.txtDriver 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,
- addKconfigentrySigned-off-by Przemyslaw Marczak <[email protected]>CC Masahiro Yamada <[email protected]>Cc Lukasz Majewski <[email protected]>Cc Mike Frysinger <[email protected]>Cc Simon Glass <[email protected]>Cc Heiko Schocher <[email protected]>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
- fixKconfigentryChanges V3:
- fixKconfigentry
- update file header
- remove field "speed" from structi2c_gpio_bus
- move macros to functions
- move duplicated routines into functions
- update naming with prefix "i2c_gpio_"Quoted 8 lines
--- 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
Quoted 1 line
Acked-by Simon Glass <[email protected]>
Applied to u-boot-dm/next, thanks!