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

Przemyslaw Marczak p.marczak at samsung.com
Tue Mar 10 11:30:42 CET 2015


This change adds driver model support to software emulated
i2c bus driver. To bind the driver, proper device-tree node
must be defined, with the following attributes:
- alias: to keep proper bus order
- compatible: 'soft-i2c'
- clock-frequency [Hz]
- clock-pin, data-pin: gpio phandles

/* Define the alias number to keep bus numbering order */
aliases {
	[...]
	i2c5 = "/i2c at 13500000";
	i2c6 = "/soft-i2c at 1";
	[...]
};

/* Define the basic bus attributes */
soft-i2c at 1 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "soft-i2c";
	clock-frequency = <50000>;

	/* Define the proper GPIO pins */
	clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
	data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;

	/* Optionally, define some driver node (bus child) */
	somedev at 0x44 {
		compatible = "somedev";
		reg = <0x44>;
		[...]
	};
};

The device can be accessed by the i2c command:
 i2c dev 8                   (bus number set by alias)
 i2c probe <0x44>            (address is optionally)
 i2c md 0x44 0x0             (dump dev registers at address 0x0)
 Valid chip addresses: 0x44  (success!)
 ...

The previous driver functionality stays unchanged. Driving the bus lines
is done by dm gpio calls in the preprocessor macros. Each, can be redefined
by the user as previous.

Tested on Trats2 and Odroid U3.

Signed-off-by: Przemyslaw Marczak <p.marczak at samsung.com>
Cc: Masahiro Yamada <yamada.m at jp.panasonic.com>
Cc: Mike Frysinger <vapier at gentoo.org>
Cc: Simon Glass <sjg at chromium.org>
Cc: Heiko Schocher <hs at denx.de>
---
 drivers/i2c/Makefile   |   1 +
 drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 400 insertions(+), 11 deletions(-)

diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 774bc94..7039b6d 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -6,6 +6,7 @@
 #
 obj-$(CONFIG_DM_I2C) += i2c-uclass.o
 obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
+obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
 
 obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
 obj-$(CONFIG_I2C_MV) += mv_i2c.o
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index db9b402..7afae0b 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -1,4 +1,7 @@
 /*
+ * (C) Copyright 2015, Samsung Electronics
+ * Przemyslaw Marczak <p.marczak at samsung.com>
+ *
  * (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);
+		I2C_DELAY(delay);
+		I2C_DELAY(delay);
+	}
+	send_stop(scl, sda, delay);
+	I2C_TRISTATE(sda);
+}
+#endif /* CONFIG_SYS_I2C_SOFT */
 #endif
 
 /*-----------------------------------------------------------------------
  * START: High -> Low on SDA while SCL is High
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static void send_start(void)
 {
 	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
@@ -184,10 +283,25 @@ static void send_start(void)
 	I2C_SDA(0);
 	I2C_DELAY;
 }
+#else
+static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
+{
+	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
 
+	I2C_DELAY(delay);
+	I2C_SDA(sda, 1);
+	I2C_ACTIVE(sda);
+	I2C_DELAY(delay);
+	I2C_SCL(scl, 1);
+	I2C_DELAY(delay);
+	I2C_SDA(sda, 0);
+	I2C_DELAY(delay);
+}
+#endif /* CONFIG_SYS_I2C_SOFT */
 /*-----------------------------------------------------------------------
  * STOP: Low -> High on SDA while SCL is High
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static void send_stop(void)
 {
 	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
@@ -203,10 +317,28 @@ static void send_stop(void)
 	I2C_DELAY;
 	I2C_TRISTATE;
 }
+#else
+static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
+{
+	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
+
+	I2C_SCL(scl, 0);
+	I2C_DELAY(delay);
+	I2C_SDA(sda, 0);
+	I2C_ACTIVE(sda);
+	I2C_DELAY(delay);
+	I2C_SCL(scl, 1);
+	I2C_DELAY(delay);
+	I2C_SDA(sda, 1);
+	I2C_DELAY(delay);
+	I2C_TRISTATE(sda);
+}
+#endif /* CONFIG_SYS_I2C_SOFT */
 
 /*-----------------------------------------------------------------------
  * ack should be I2C_ACK or I2C_NOACK
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static void send_ack(int ack)
 {
 	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
@@ -222,10 +354,29 @@ static void send_ack(int ack)
 	I2C_SCL(0);
 	I2C_DELAY;
 }
+#else
+static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
+		     int delay, int ack)
+{
+	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
+
+	I2C_SCL(scl, 0);
+	I2C_DELAY(delay);
+	I2C_ACTIVE(sda);
+	I2C_SDA(sda, ack);
+	I2C_DELAY(delay);
+	I2C_SCL(scl, 1);
+	I2C_DELAY(delay);
+	I2C_DELAY(delay);
+	I2C_SCL(scl, 0);
+	I2C_DELAY(delay);
+}
+#endif
 
 /*-----------------------------------------------------------------------
  * Send 8 bits and look for an acknowledgement.
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static int write_byte(uchar data)
 {
 	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
@@ -263,11 +414,52 @@ static int write_byte(uchar data)
 
 	return(nack);	/* not a nack is an ack */
 }
+#else
+static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
+		      int delay, uchar data)
+{
+	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
+	int j;
+	int nack;
+
+	I2C_ACTIVE(sda);
+	for (j = 0; j < 8; j++) {
+		I2C_SCL(scl, 0);
+		I2C_DELAY(delay);
+		I2C_SDA(sda, data & 0x80);
+		I2C_DELAY(delay);
+		I2C_SCL(scl, 1);
+		I2C_DELAY(delay);
+		I2C_DELAY(delay);
+
+		data <<= 1;
+	}
+
+	/*
+	 * Look for an <ACK>(negative logic) and return it.
+	 */
+	I2C_SCL(scl, 0);
+	I2C_DELAY(delay);
+	I2C_SDA(sda, 1);
+	I2C_TRISTATE(sda);
+	I2C_DELAY(delay);
+	I2C_SCL(scl, 1);
+	I2C_DELAY(delay);
+	I2C_DELAY(delay);
+	nack = I2C_READ(sda);
+	I2C_SCL(scl, 0);
+	I2C_DELAY(delay);
+	I2C_ACTIVE(sda);
+
+	return nack;	/* not a nack is an ack */
+}
+#endif
 
 /*-----------------------------------------------------------------------
  * if ack == I2C_ACK, ACK the byte so can continue reading, else
  * send I2C_NOACK to end the read.
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static uchar read_byte(int ack)
 {
 	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
@@ -293,10 +485,38 @@ static uchar read_byte(int ack)
 
 	return(data);
 }
+#else
+static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
+		       int delay, int ack)
+{
+	I2C_SOFT_DECLARATIONS	/* intentional without ';' */
+	int  data;
+	int  j;
+
+	/*
+	 * Read 8 bits, MSB first.
+	 */
+	I2C_TRISTATE(sda);
+	I2C_SDA(sda, 1);
+	data = 0;
+	for (j = 0; j < 8; j++) {
+		I2C_SCL(scl, 0);
+		I2C_DELAY(delay);
+		I2C_SCL(scl, 1);
+		I2C_DELAY(delay);
+		data <<= 1;
+		data |= I2C_READ(sda);
+		I2C_DELAY(delay);
+	}
+	send_ack(scl, sda, delay, ack);
 
+	return data;
+}
+#endif /* CONFIG_SYS_I2C_SOFT */
 /*-----------------------------------------------------------------------
  * Initialization
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
 {
 #if defined(CONFIG_SYS_I2C_INIT_BOARD)
@@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
 	send_reset ();
 #endif
 }
+#endif
 
 /*-----------------------------------------------------------------------
  * Probe to see if a chip is present.  Also good for checking for the
  * completion of EEPROM writes since the chip stops responding until
  * the write completes (typically 10mSec).
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
 {
 	int rc;
@@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
 
 	return (rc ? 1 : 0);
 }
+#endif
 
 /*-----------------------------------------------------------------------
  * Read bytes
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
 			int alen, uchar *buffer, int len)
 {
@@ -409,10 +633,12 @@ static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
 	send_stop();
 	return(0);
 }
+#endif
 
 /*-----------------------------------------------------------------------
  * Write bytes
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
 			int alen, uchar *buffer, int len)
 {
@@ -444,10 +670,12 @@ static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
 	send_stop();
 	return(failures);
 }
+#endif
 
 /*
  * Register soft i2c adapters
  */
+#ifdef CONFIG_SYS_I2C_SOFT
 U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe,
 			 soft_i2c_read, soft_i2c_write, NULL,
 			 CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
@@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe,
 			 CONFIG_SYS_I2C_SOFT_SLAVE_4,
 			 3)
 #endif
+#endif /* CONFIG_SYS_I2C_SOFT */
+
+#ifdef CONFIG_DM_I2C_SOFT
+struct soft_i2c_bus {
+	unsigned int speed;
+	unsigned long delay;
+	struct gpio_desc scl;
+	struct gpio_desc sda;
+};
+
+static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
+			  uchar *buffer, int len, bool end_with_repeated_start)
+{
+	struct gpio_desc *scl = &i2c_bus->scl;
+	struct gpio_desc *sda = &i2c_bus->sda;
+	unsigned int delay = i2c_bus->delay;
+	int failures = 0;
+
+	PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
+
+	send_start(scl, sda, delay);
+	if (write_byte(scl, sda, delay, chip << 1)) {
+		send_stop(scl, sda, delay);
+		PRINTD("i2c_write, no chip responded %02X\n", chip);
+		return -ENODEV;
+	}
+
+	while (len-- > 0) {
+		if (write_byte(scl, sda, delay, *buffer++))
+			failures++;
+	}
+
+	send_stop(scl, sda, delay);
+
+	return failures;
+}
+
+static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
+			 uchar *buffer, int len)
+{
+	struct gpio_desc *scl = &i2c_bus->scl;
+	struct gpio_desc *sda = &i2c_bus->sda;
+	unsigned int delay = i2c_bus->delay;
+
+	PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
+
+	send_start(scl, sda, delay);
+	write_byte(scl, sda, delay, (chip << 1) | 1);	/* read cycle */
+
+	while (len-- > 0)
+		*buffer++ = read_byte(scl, sda, delay, len == 0);
+
+	send_stop(scl, sda, delay);
+
+	return 0;
+}
+
+static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
+{
+	struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
+	int ret;
+
+	for (; nmsgs > 0; nmsgs--, msg++) {
+		bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
+		if (msg->flags & I2C_M_RD) {
+			ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
+					    msg->len);
+		} else {
+			ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
+					     msg->len, next_is_read);
+		}
+		if (ret)
+			return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags)
+{
+	struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
+	struct gpio_desc *scl = &i2c_bus->scl;
+	struct gpio_desc *sda = &i2c_bus->sda;
+	unsigned int delay = i2c_bus->delay;
+	int ret;
+
+	send_start(scl, sda, delay);
+	ret = write_byte(scl, sda, delay, (chip << 1) | 0);
+	send_stop(scl, sda, delay);
+
+	PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
+	       __func__, dev->seq, dev->name, chip, chip_flags, ret);
+
+	return ret;
+}
+
+static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
+{
+	struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
+	struct gpio_desc *scl = &i2c_bus->scl;
+	struct gpio_desc *sda = &i2c_bus->sda;
+
+	i2c_bus->speed = speed;
+	i2c_bus->delay = lldiv(1000000, speed << 2);
+
+	send_reset(scl, sda, i2c_bus->delay);
+
+	PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
+	       __func__, dev->seq, dev->name, speed, i2c_bus->delay);
+
+	return 0;
+}
+
+static int soft_i2c_ofdata_to_platdata(struct udevice *dev)
+{
+	struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
+	const void *blob = gd->fdt_blob;
+	char *pin_name;
+	int ret;
+
+	pin_name = "clock-pin";
+	ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
+					 0, &i2c_bus->scl, GPIOD_IS_OUT);
+	if (ret)
+		goto error;
+
+	pin_name = "data-pin";
+	ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
+					 0, &i2c_bus->sda, GPIOD_IS_OUT);
+	if (ret)
+		goto error;
+
+	PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
+
+	return 0;
+error:
+	error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
+	return ret;
+}
+
+static const struct dm_i2c_ops soft_i2c_ops = {
+	.xfer		= soft_i2c_xfer,
+	.probe_chip	= soft_i2c_probe,
+	.set_bus_speed	= soft_i2c_set_bus_speed,
+};
+
+static const struct udevice_id soft_i2c_ids[] = {
+	{ .compatible = "soft-i2c" },
+	{ }
+};
+
+U_BOOT_DRIVER(soft_i2c) = {
+	.name	= "soft-i2c",
+	.id	= UCLASS_I2C,
+	.of_match = soft_i2c_ids,
+	.ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
+	.ops	= &soft_i2c_ops,
+};
+#endif /* CONFIG_DM_I2C_SOFT */
-- 
1.9.1



More information about the U-Boot mailing list