Skip to contentSkip to conversation

dm: i2c: enable driver model for software i2c

PATCH Superseded Przemyslaw Marczak 5 participants 36 messages Mar 10, 2015 – Apr 1, 2015

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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.

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
index 774bc94..7039b6d 100644
@@ -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
#2 lore raw

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.

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
index 774bc94..7039b6d 100644
@@ -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 +8 -0
index db9b402..7afae0b 100644
@@ -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_NOACK 1 /* 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); #endif

drivers/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)
#endif

drivers/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 int

speed) +{

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: %lu

us)\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
#3 lore raw

PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver

In reply to Przemyslaw Marczak

Hi 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
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
index 774bc94..7039b6d 100644
@@ -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 +97 -11
index db9b402..7afae0b 100644
@@ -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

#4 lore raw

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
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
index 774bc94..7039b6d 100644
@@ -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 +102 -11
index db9b402..7afae0b 100644
@@ -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;

-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

Signature · 2 lines
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
#5 lore raw

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
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
index 774bc94..7039b6d 100644
@@ -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 +69 -3
index db9b402..7afae0b 100644
@@ -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 */
#define I2C_NOACK 1 /* PD_SDA level 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
#6 lore raw

PATCH 1/3 dm: i2c soft: enable driver model for software i2c driver

In reply to Simon Glass

Hello 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
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
index 774bc94..7039b6d 100644
@@ -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 +102 -11
index db9b402..7afae0b 100644
@@ -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;

-EIO I 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
#7 lore raw

PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c

In reply to Przemyslaw Marczak
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 +1 -10
index 692810d..0a52ed9 100644
@@ -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
#8 lore raw

PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c

Hi Przemyslaw,

Quoted 21 lines
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
index 692810d..0a52ed9 100644
@@ -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
#9 lore raw

PATCH 2/3 Kconfig: i2c: remove wrong help message related to dm i2c

In reply to Przemyslaw Marczak

Hi,

Quoted 18 lines · Przemyslaw Marczak
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
index 692810d..0a52ed9 100644
@@ -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 -1
 config DM_I2C_COMPAT
        bool "Enable I2C compatibility layer"
--

1.9.1

Regards,
Simon

#10 lore raw

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
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
index 692810d..0a52ed9 100644
@@ -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 :-)

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 -1
  config 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
#11 lore raw

PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c

In reply to Przemyslaw Marczak
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 +43 -0
index 0a52ed9..dd7eb3c 100644
@@ -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
#12 lore raw

PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c

Hi Przemyslaw,

Quoted 61 lines
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
index 0a52ed9..dd7eb3c 100644
@@ -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.

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, like

below. +

drivers/i2c/Kconfig +1 -0
+	  /* First, define the alias number to have continuous bus

numbering */

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 at

address 0x0)

drivers/i2c/Kconfig +4 -0
+	  # Valid chip addresses: 0x44  (success!)
+	  ...
+
+	  Driving the bus lines is done by dm gpio calls in the

preprocessor

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
#13 lore raw

PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c

In reply to Przemyslaw Marczak
Quoted 9 lines · Przemyslaw Marczak
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.

#14 lore raw

PATCH 3/3 Kconfig: i2c: add entry for driver-model software i2c

In reply to Przemyslaw Marczak

Hi.

2015-03-10 19:30 GMT+09:00 Przemyslaw Marczak <[email protected]>:

Quoted 2 lines

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
index 0a52ed9..dd7eb3c 100644

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
#15 lore raw

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

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 -0
index 0a52ed9..dd7eb3c 100644

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";
+           [...]
+         }
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
#16 lore raw

v2 cover dm: i2c: enable driver model for i2c gpio

In reply to Przemyslaw Marczak

This patchset enables driver model support for software i2c bus driver. It was tested on Trats2 and Odroid U3 devices.

It can be tested on any other device by just modifying the dts file, first by disabling the hardware i2c bus and then, as it is described in the doc binding file, add i2c-gpio node.

The drivers, which are using the old api are not converted with this patchset. I hope that maintainers will do this if required.

Probably the software i2c is used for PMIC devices not only for Trats2, or Universal C210, so I suggest to wait with moving the drivers until the pmic is done - this will prevent adding temporary code.

Przemyslaw Marczak (3):
dm: gpio: request list: return the count if requests max_count reached
Kconfig: i2c: fix help message related to dm i2c
dm: i2c: add i2c-gpio driver

 doc/device-tree-bindings/i2c/i2c-gpio.txt |  37 ++++
 drivers/gpio/gpio-uclass.c                |   6 +-
 drivers/i2c/Kconfig                       |  26 ++-
 drivers/i2c/Makefile                      |   1 +
 drivers/i2c/i2c-gpio.c                    | 353 ++++++++++++++++++++++++++++++
 5 files changed, 408 insertions(+), 15 deletions(-)
 create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt
 create mode 100644 drivers/i2c/i2c-gpio.c
Signature · 1 line
1.9.1
#17 lore raw

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
index a69bbd2..4b63025 100644
@@ -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
#18 lore raw

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 -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.

Quoted 5 lines
---
 drivers/gpio/gpio-uclass.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
#19 lore raw

PATCH v2 2/3 Kconfig: i2c: fix help message related to dm i2c

In reply to Przemyslaw Marczak
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(-)
drivers/i2c/Kconfig +7 -10
index 692810d..979522f 100644
@@ -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
#20 lore raw

PATCH v2 2/3 Kconfig: i2c: fix help message related to dm i2c

Hi Przemyslaw,

Quoted 11 lines · Przemyslaw Marczak
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
index 692810d..979522f 100644
@@ -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

#21 lore raw

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
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
index 692810d..979522f 100644
@@ -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?

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 -1
  config 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
#22 lore raw

PATCH v2 3/3 dm: i2c: add i2c-gpio driver

In reply to 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.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

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
- 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
new file mode 100644
index 0000000..3978381
@@ -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
index 979522f..22e4a7c 100644
@@ -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
index 774bc94..d9e9f3a 100644
@@ -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
#23 lore raw

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.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

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
- fix Kconfig entry

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.c
doc/device-tree-bindings/i2c/i2c-gpio.txt new file +37 -0
new file mode 100644
index 0000000..3978381
@@ -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
index 979522f..22e4a7c 100644

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_I2C
drivers/i2c/Makefile +1 -0
index 774bc94..d9e9f3a 100644
@@ -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 +27 -0
new file mode 100644
index 0000000..8e9ed6b
@@ -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

#24 lore raw

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.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

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
- fix Kconfig entry

Sorry 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.txt create mode 100644 drivers/i2c/i2c-gpio.c

doc/device-tree-bindings/i2c/i2c-gpio.txt new file +37 -0
new file mode 100644
index 0000000..3978381
@@ -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
index 979522f..22e4a7c 100644

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.
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_I2C
drivers/i2c/Makefile +1 -0
index 774bc94..d9e9f3a 100644
@@ -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 +31 -0
new file mode 100644
index 0000000..8e9ed6b
@@ -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
#25 lore raw

v3 cover dm: i2c: enable driver model for i2c gpio

In reply to Przemyslaw Marczak

This patchset enables driver model support for software i2c bus driver. It was tested on Trats2 and Odroid U3 devices.

It can be tested on any other device by just modifying the dts file, first by disabling the hardware i2c bus and then, as it is described in the doc binding file, add i2c-gpio node.

The drivers, which are using the old api are not converted with this patchset. I hope that maintainers will do this if required.

Probably the software i2c is used for PMIC devices not only for Trats2, or Universal C210, so I suggest to wait with moving the drivers until the pmic is done - this will prevent adding temporary code.

Przemyslaw Marczak (3):
dm: gpio: request list: return the count if requests max_count reached
Kconfig: i2c: fix help message related to dm i2c
dm: i2c: add i2c-gpio driver

 doc/device-tree-bindings/i2c/i2c-gpio.txt |  37 ++++
 drivers/gpio/gpio-uclass.c                |   6 +-
 drivers/i2c/Kconfig                       |  26 ++-
 drivers/i2c/Makefile                      |   1 +
 drivers/i2c/i2c-gpio.c                    | 345 ++++++++++++++++++++++++++++++
 5 files changed, 400 insertions(+), 15 deletions(-)
 create mode 100644 doc/device-tree-bindings/i2c/i2c-gpio.txt
 create mode 100644 drivers/i2c/i2c-gpio.c
Signature · 1 line
1.9.1
#26 lore raw

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
index a69bbd2..4b63025 100644
@@ -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
#27 lore raw

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 -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

There's something odd about the change logs here - they are coming in as part of the commit message. Are you using patman?

#28 lore raw

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 -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.

Quoted 7 lines
Quoted 2 lines

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?

Applied to u-boot-dm/next, thanks!

#29 lore raw

PATCH v3 1/3 dm: gpio: request list: return the count if requests max_count reached

In reply to Simon Glass

Hello Simon,

Quoted 10 lines · Simon Glass
Quoted 9 lines · Przemyslaw Marczak

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.

Quoted 8 lines
Quoted 2 lines

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?

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
#30 lore raw

PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c

In reply to Przemyslaw Marczak
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

Changes v3:
- Kconfig: i2c-uclass: fix help message

---
 drivers/i2c/Kconfig | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/i2c/Kconfig +7 -10
index 692810d..c83a984 100644
@@ -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
#31 lore raw

PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c

Quoted 44 lines · Przemyslaw Marczak
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

Changes v3:
- Kconfig: i2c-uclass: fix help message

---
 drivers/i2c/Kconfig | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/i2c/Kconfig +7 -11
index 692810d..c83a984 100644
@@ -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

#32 lore raw

PATCH v3 2/3 Kconfig: i2c: fix help message related to dm i2c

Quoted 42 lines · Simon Glass
Quoted 41 lines · Przemyslaw Marczak
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

Changes v3:
- Kconfig: i2c-uclass: fix help message

---
 drivers/i2c/Kconfig | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)
drivers/i2c/Kconfig +7 -10
index 692810d..c83a984 100644
@@ -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 -1
 config DM_I2C_COMPAT
        bool "Enable I2C compatibility layer"
--

1.9.1

Applied to u-boot-dm/next, thanks!

#33 lore raw

PATCH v3 3/3 dm: i2c: add i2c-gpio driver

In reply to 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.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

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
- 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
new file mode 100644
index 0000000..ba56ed5
@@ -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
index c83a984..739badc 100644
@@ -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
index 774bc94..d9e9f3a 100644
@@ -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
#34 lore raw

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.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

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
- 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_"

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
#35 lore raw

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.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

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
- 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_"

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

Applied to u-boot-dm/next, thanks!