diff mbox

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

Message ID 1425983444-18565-2-git-send-email-p.marczak@samsung.com
State Changes Requested
Delegated to: Simon Glass
Headers show

Commit Message

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

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

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

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

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

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

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

Tested on Trats2 and Odroid U3.

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

Comments

Ɓukasz Majewski March 23, 2015, 8:44 a.m. UTC | #1
Hi Przemyslaw,

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

Reviewed-by: Lukasz Majewski <l.majewski@samsung.com>
Simon Glass March 23, 2015, 11:38 p.m. UTC | #2
Hi Przemyslaw,

On 10 March 2015 at 04:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
> This change adds driver model support to software emulated
> i2c bus driver. To bind the driver, proper device-tree node
> must be defined, with the following attributes:
> - alias: to keep proper bus order
> - compatible: 'soft-i2c'
> - clock-frequency [Hz]
> - clock-pin, data-pin: gpio phandles
>
> /* Define the alias number to keep bus numbering order */
> aliases {
>         [...]
>         i2c5 = "/i2c@13500000";
>         i2c6 = "/soft-i2c@1";
>         [...]
> };
>
> /* Define the basic bus attributes */
> soft-i2c@1 {
>         #address-cells = <1>;
>         #size-cells = <0>;
>         compatible = "soft-i2c";
>         clock-frequency = <50000>;
>
>         /* Define the proper GPIO pins */
>         clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
>         data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
>
>         /* Optionally, define some driver node (bus child) */
>         somedev@0x44 {
>                 compatible = "somedev";
>                 reg = <0x44>;
>                 [...]
>         };
> };
>
> The device can be accessed by the i2c command:
>  i2c dev 8                   (bus number set by alias)
>  i2c probe <0x44>            (address is optionally)
>  i2c md 0x44 0x0             (dump dev registers at address 0x0)
>  Valid chip addresses: 0x44  (success!)
>  ...
>
> The previous driver functionality stays unchanged. Driving the bus lines
> is done by dm gpio calls in the preprocessor macros. Each, can be redefined
> by the user as previous.
>
> Tested on Trats2 and Odroid U3.

Is the intention to retire the old (non-driver-model) code? What boards use it?

>
> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
> Cc: Masahiro Yamada <yamada.m@jp.panasonic.com>
> Cc: Mike Frysinger <vapier@gentoo.org>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Heiko Schocher <hs@denx.de>
> ---
>  drivers/i2c/Makefile   |   1 +
>  drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 400 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index 774bc94..7039b6d 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -6,6 +6,7 @@
>  #
>  obj-$(CONFIG_DM_I2C) += i2c-uclass.o
>  obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
> +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
>
>  obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
>  obj-$(CONFIG_I2C_MV) += mv_i2c.o
> diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
> index db9b402..7afae0b 100644
> --- a/drivers/i2c/soft_i2c.c
> +++ b/drivers/i2c/soft_i2c.c
> @@ -1,4 +1,7 @@
>  /*
> + * (C) Copyright 2015, Samsung Electronics
> + * Przemyslaw Marczak <p.marczak@samsung.com>
> + *
>   * (C) Copyright 2009
>   * Heiko Schocher, DENX Software Engineering, hs@denx.de.
>   * Changes for multibus/multiadapter I2C support.
> @@ -14,6 +17,11 @@
>   */
>
>  #include <common.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <div64.h>
> +#include <asm/gpio.h>
>  #ifdef CONFIG_MPC8260                  /* only valid for MPC8260 */
>  #include <ioports.h>
>  #include <asm/io.h>
> @@ -32,11 +40,9 @@
>  #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
>  #include <asm/io.h>
>  #endif
> -#include <i2c.h>
>
> +#if defined(CONFIG_SYS_I2C)
>  #if defined(CONFIG_SOFT_I2C_GPIO_SCL)
> -# include <asm/gpio.h>
> -
>  # ifndef I2C_GPIO_SYNC
>  #  define I2C_GPIO_SYNC
>  # endif
> @@ -85,6 +91,7 @@
>  # endif
>
>  #endif
> +#endif
>
>  /* #define     DEBUG_I2C       */
>
> @@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR;
>  #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE
>  #endif
>
> +/* DM SOFT I2C */
> +#ifdef CONFIG_DM_I2C_SOFT
> +# ifndef I2C_GPIO_SYNC
> +#  define I2C_GPIO_SYNC(gpio)
> +# endif
> +
> +# ifndef I2C_INIT
> +#  define I2C_INIT(scl, sda)  \
> +       do { } while (0)
> +# endif
> +
> +# ifndef I2C_ACTIVE
> +#  define I2C_ACTIVE(sda) \
> +       do { } while (0)
> +# endif
> +
> +# ifndef I2C_TRISTATE
> +#  define I2C_TRISTATE(sda) \
> +       do { } while (0)
> +# endif
> +
> +# ifndef I2C_READ
> +#  define I2C_READ(sda) dm_gpio_get_value(sda);
> +# endif
> +
> +# ifndef I2C_SDA
> +#  define I2C_SDA(sda, bit) \
> +       do { \
> +               if (bit) { \
> +                       sda->flags &= ~GPIOD_IS_OUT; \
> +                       sda->flags |= GPIOD_IS_IN; \
> +                       dm_gpio_set_dir(sda); \
> +               } else { \
> +                       sda->flags &= ~GPIOD_IS_IN; \
> +                       sda->flags |= GPIOD_IS_OUT; \
> +                       dm_gpio_set_dir(sda); \
> +                       dm_gpio_set_value(sda, 0); \
> +               } \
> +               I2C_GPIO_SYNC(sda); \
> +       } while (0)
> +# endif
> +
> +# ifndef I2C_SCL
> +#  define I2C_SCL(scl, bit) \
> +       do { \
> +               scl->flags &= ~GPIOD_IS_IN; \
> +               scl->flags |= GPIOD_IS_OUT; \
> +               dm_gpio_set_dir(scl); \
> +               dm_gpio_set_value(scl, bit); \
> +               I2C_GPIO_SYNC(scl); \
> +       } while (0)
> +# endif
> +
> +# ifndef I2C_DELAY
> +#  define I2C_DELAY(us) udelay(us)     /* 1/4 I2C clock duration */
> +# endif
> +
> +#endif /* CONFIG_DM_I2C_SOFT */
> +
>  /*-----------------------------------------------------------------------
>   * Definitions
>   */
> @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR;
>  #define I2C_ACK                0               /* PD_SDA level to ack a byte */
>  #define I2C_NOACK      1               /* PD_SDA level to noack a byte */
>
> -
>  #ifdef DEBUG_I2C
>  #define PRINTD(fmt,args...)    do {    \
>                 printf (fmt ,##args);   \
> @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR;
>  /*-----------------------------------------------------------------------
>   * Local functions
>   */
> +#ifdef CONFIG_SYS_I2C
>  #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
> -static void  send_reset        (void);
> +static void send_reset(void);
> +#endif
> +static void send_start(void);
> +static void send_stop(void);
> +static void send_ack(int);
> +static int write_byte(uchar byte);
> +static uchar read_byte(int);
> +#else
> +static void send_reset(struct gpio_desc *, struct gpio_desc *, int);
> +static void send_start(struct gpio_desc *, struct gpio_desc *, int);
> +static void send_stop(struct gpio_desc *, struct gpio_desc *, int);
> +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int);
> +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar);
> +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int);
>  #endif
> -static void  send_start        (void);
> -static void  send_stop (void);
> -static void  send_ack  (int);
> -static int   write_byte        (uchar byte);
> -static uchar read_byte (int);
> -
>  #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>  /*-----------------------------------------------------------------------
>   * Send a reset sequence consisting of 9 clocks with the data signal high
>   * to clock any confused device back into an idle state.  Also send a
>   * <stop> at the end of the sequence for belts & suspenders.
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static void send_reset(void)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -166,11 +240,36 @@ static void send_reset(void)
>         send_stop();
>         I2C_TRISTATE;
>  }
> +#else
> +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> +       int j;
> +
> +       I2C_SCL(scl, 1);
> +       I2C_SDA(sda, 1);
> +#ifdef I2C_INIT
> +       I2C_INIT(scl, sda);
> +#endif
> +       I2C_TRISTATE(sda);
> +       for (j = 0; j < 9; j++) {
> +               I2C_SCL(scl, 0);
> +               I2C_DELAY(delay);
> +               I2C_DELAY(delay);
> +               I2C_SCL(scl, 1);
> +               I2C_DELAY(delay);
> +               I2C_DELAY(delay);
> +       }
> +       send_stop(scl, sda, delay);
> +       I2C_TRISTATE(sda);

For the new code I would much prefer that these become functions
rather than macros. What is the benefit of using a macro?

> +}
> +#endif /* CONFIG_SYS_I2C_SOFT */
>  #endif
>
>  /*-----------------------------------------------------------------------
>   * START: High -> Low on SDA while SCL is High
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static void send_start(void)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -184,10 +283,25 @@ static void send_start(void)
>         I2C_SDA(0);
>         I2C_DELAY;
>  }
> +#else
> +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>
> +       I2C_DELAY(delay);
> +       I2C_SDA(sda, 1);
> +       I2C_ACTIVE(sda);
> +       I2C_DELAY(delay);
> +       I2C_SCL(scl, 1);
> +       I2C_DELAY(delay);
> +       I2C_SDA(sda, 0);
> +       I2C_DELAY(delay);
> +}
> +#endif /* CONFIG_SYS_I2C_SOFT */
>  /*-----------------------------------------------------------------------
>   * STOP: Low -> High on SDA while SCL is High
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static void send_stop(void)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -203,10 +317,28 @@ static void send_stop(void)
>         I2C_DELAY;
>         I2C_TRISTATE;
>  }
> +#else
> +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> +
> +       I2C_SCL(scl, 0);
> +       I2C_DELAY(delay);
> +       I2C_SDA(sda, 0);
> +       I2C_ACTIVE(sda);
> +       I2C_DELAY(delay);
> +       I2C_SCL(scl, 1);
> +       I2C_DELAY(delay);
> +       I2C_SDA(sda, 1);
> +       I2C_DELAY(delay);
> +       I2C_TRISTATE(sda);
> +}
> +#endif /* CONFIG_SYS_I2C_SOFT */
>
>  /*-----------------------------------------------------------------------
>   * ack should be I2C_ACK or I2C_NOACK
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static void send_ack(int ack)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -222,10 +354,29 @@ static void send_ack(int ack)
>         I2C_SCL(0);
>         I2C_DELAY;
>  }
> +#else
> +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
> +                    int delay, int ack)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> +
> +       I2C_SCL(scl, 0);
> +       I2C_DELAY(delay);
> +       I2C_ACTIVE(sda);
> +       I2C_SDA(sda, ack);
> +       I2C_DELAY(delay);
> +       I2C_SCL(scl, 1);
> +       I2C_DELAY(delay);
> +       I2C_DELAY(delay);
> +       I2C_SCL(scl, 0);
> +       I2C_DELAY(delay);
> +}
> +#endif
>
>  /*-----------------------------------------------------------------------
>   * Send 8 bits and look for an acknowledgement.
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static int write_byte(uchar data)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -263,11 +414,52 @@ static int write_byte(uchar data)
>
>         return(nack);   /* not a nack is an ack */
>  }
> +#else
> +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
> +                     int delay, uchar data)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> +       int j;
> +       int nack;
> +
> +       I2C_ACTIVE(sda);
> +       for (j = 0; j < 8; j++) {
> +               I2C_SCL(scl, 0);
> +               I2C_DELAY(delay);
> +               I2C_SDA(sda, data & 0x80);
> +               I2C_DELAY(delay);
> +               I2C_SCL(scl, 1);
> +               I2C_DELAY(delay);
> +               I2C_DELAY(delay);
> +
> +               data <<= 1;
> +       }
> +
> +       /*
> +        * Look for an <ACK>(negative logic) and return it.
> +        */
> +       I2C_SCL(scl, 0);
> +       I2C_DELAY(delay);
> +       I2C_SDA(sda, 1);
> +       I2C_TRISTATE(sda);
> +       I2C_DELAY(delay);
> +       I2C_SCL(scl, 1);
> +       I2C_DELAY(delay);
> +       I2C_DELAY(delay);
> +       nack = I2C_READ(sda);
> +       I2C_SCL(scl, 0);
> +       I2C_DELAY(delay);
> +       I2C_ACTIVE(sda);
> +
> +       return nack;    /* not a nack is an ack */
> +}
> +#endif
>
>  /*-----------------------------------------------------------------------
>   * if ack == I2C_ACK, ACK the byte so can continue reading, else
>   * send I2C_NOACK to end the read.
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static uchar read_byte(int ack)
>  {
>         I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
>
>         return(data);
>  }
> +#else
> +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
> +                      int delay, int ack)
> +{
> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
> +       int  data;
> +       int  j;
> +
> +       /*
> +        * Read 8 bits, MSB first.
> +        */
> +       I2C_TRISTATE(sda);
> +       I2C_SDA(sda, 1);
> +       data = 0;
> +       for (j = 0; j < 8; j++) {
> +               I2C_SCL(scl, 0);
> +               I2C_DELAY(delay);
> +               I2C_SCL(scl, 1);
> +               I2C_DELAY(delay);
> +               data <<= 1;
> +               data |= I2C_READ(sda);
> +               I2C_DELAY(delay);
> +       }
> +       send_ack(scl, sda, delay, ack);
>
> +       return data;
> +}
> +#endif /* CONFIG_SYS_I2C_SOFT */
>  /*-----------------------------------------------------------------------
>   * Initialization
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>  {
>  #if defined(CONFIG_SYS_I2C_INIT_BOARD)
> @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>         send_reset ();
>  #endif
>  }
> +#endif
>
>  /*-----------------------------------------------------------------------
>   * Probe to see if a chip is present.  Also good for checking for the
>   * completion of EEPROM writes since the chip stops responding until
>   * the write completes (typically 10mSec).
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
>  {
>         int rc;
> @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
>
>         return (rc ? 1 : 0);
>  }
> +#endif
>
>  /*-----------------------------------------------------------------------
>   * Read bytes
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
>                         int alen, uchar *buffer, int len)
>  {
> @@ -409,10 +633,12 @@ static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
>         send_stop();
>         return(0);
>  }
> +#endif
>
>  /*-----------------------------------------------------------------------
>   * Write bytes
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
>                         int alen, uchar *buffer, int len)
>  {
> @@ -444,10 +670,12 @@ static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
>         send_stop();
>         return(failures);
>  }
> +#endif
>
>  /*
>   * Register soft i2c adapters
>   */
> +#ifdef CONFIG_SYS_I2C_SOFT
>  U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe,
>                          soft_i2c_read, soft_i2c_write, NULL,
>                          CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
> @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe,
>                          CONFIG_SYS_I2C_SOFT_SLAVE_4,
>                          3)
>  #endif
> +#endif /* CONFIG_SYS_I2C_SOFT */
> +
> +#ifdef CONFIG_DM_I2C_SOFT
> +struct soft_i2c_bus {
> +       unsigned int speed;
> +       unsigned long delay;
> +       struct gpio_desc scl;
> +       struct gpio_desc sda;
> +};
> +
> +static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
> +                         uchar *buffer, int len, bool end_with_repeated_start)
> +{
> +       struct gpio_desc *scl = &i2c_bus->scl;
> +       struct gpio_desc *sda = &i2c_bus->sda;
> +       unsigned int delay = i2c_bus->delay;
> +       int failures = 0;
> +
> +       PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
> +
> +       send_start(scl, sda, delay);
> +       if (write_byte(scl, sda, delay, chip << 1)) {
> +               send_stop(scl, sda, delay);
> +               PRINTD("i2c_write, no chip responded %02X\n", chip);
> +               return -ENODEV;

-EIO I think

> +       }
> +
> +       while (len-- > 0) {
> +               if (write_byte(scl, sda, delay, *buffer++))
> +                       failures++;
> +       }
> +
> +       send_stop(scl, sda, delay);
> +
> +       return failures;
> +}
> +
> +static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
> +                        uchar *buffer, int len)
> +{
> +       struct gpio_desc *scl = &i2c_bus->scl;
> +       struct gpio_desc *sda = &i2c_bus->sda;
> +       unsigned int delay = i2c_bus->delay;
> +
> +       PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
> +
> +       send_start(scl, sda, delay);
> +       write_byte(scl, sda, delay, (chip << 1) | 1);   /* read cycle */
> +
> +       while (len-- > 0)
> +               *buffer++ = read_byte(scl, sda, delay, len == 0);
> +
> +       send_stop(scl, sda, delay);
> +
> +       return 0;
> +}
> +
> +static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
> +{
> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
> +       int ret;
> +
> +       for (; nmsgs > 0; nmsgs--, msg++) {
> +               bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
> +               if (msg->flags & I2C_M_RD) {
> +                       ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
> +                                           msg->len);
> +               } else {
> +                       ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
> +                                            msg->len, next_is_read);
> +               }
> +               if (ret)
> +                       return -EREMOTEIO;
> +       }
> +
> +       return 0;
> +}
> +
> +static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags)
> +{
> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
> +       struct gpio_desc *scl = &i2c_bus->scl;
> +       struct gpio_desc *sda = &i2c_bus->sda;
> +       unsigned int delay = i2c_bus->delay;
> +       int ret;
> +
> +       send_start(scl, sda, delay);
> +       ret = write_byte(scl, sda, delay, (chip << 1) | 0);
> +       send_stop(scl, sda, delay);
> +
> +       PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
> +              __func__, dev->seq, dev->name, chip, chip_flags, ret);
> +
> +       return ret;
> +}
> +
> +static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
> +{
> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
> +       struct gpio_desc *scl = &i2c_bus->scl;
> +       struct gpio_desc *sda = &i2c_bus->sda;
> +
> +       i2c_bus->speed = speed;
> +       i2c_bus->delay = lldiv(1000000, speed << 2);
> +
> +       (scl, sda, i2c_bus->delay);
> +
> +       PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
> +              __func__, dev->seq, dev->name, speed, i2c_bus->delay);
> +
> +       return 0;
> +}
> +
> +static int soft_i2c_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
> +       const void *blob = gd->fdt_blob;
> +       char *pin_name;
> +       int ret;
> +
> +       pin_name = "clock-pin";

nit: You don't need this variable

> +       ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
> +                                        0, &i2c_bus->scl, GPIOD_IS_OUT);

You should not use the nodev version when you have a device. Also below.

> +       if (ret)
> +               goto error;
> +
> +       pin_name = "data-pin";

nit: or here

> +       ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
> +                                        0, &i2c_bus->sda, GPIOD_IS_OUT);
> +       if (ret)
> +               goto error;
> +
> +       PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
> +
> +       return 0;
> +error:
> +       error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
> +       return ret;
> +}
> +
> +static const struct dm_i2c_ops soft_i2c_ops = {
> +       .xfer           = soft_i2c_xfer,
> +       .probe_chip     = soft_i2c_probe,
> +       .set_bus_speed  = soft_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id soft_i2c_ids[] = {
> +       { .compatible = "soft-i2c" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(soft_i2c) = {
> +       .name   = "soft-i2c",
> +       .id     = UCLASS_I2C,
> +       .of_match = soft_i2c_ids,
> +       .ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
> +       .priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
> +       .ops    = &soft_i2c_ops,
> +};
> +#endif /* CONFIG_DM_I2C_SOFT */
> --
> 1.9.1
>

Regards,
Simon
Heiko Schocher March 24, 2015, 6:01 a.m. UTC | #3
Hello Simon, Przemyslaw,

Am 24.03.2015 00:38, schrieb Simon Glass:
> Hi Przemyslaw,
>
> On 10 March 2015 at 04:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This change adds driver model support to software emulated
>> i2c bus driver. To bind the driver, proper device-tree node
>> must be defined, with the following attributes:
>> - alias: to keep proper bus order
>> - compatible: 'soft-i2c'
>> - clock-frequency [Hz]
>> - clock-pin, data-pin: gpio phandles
>>
>> /* Define the alias number to keep bus numbering order */
>> aliases {
>>          [...]
>>          i2c5 = "/i2c@13500000";
>>          i2c6 = "/soft-i2c@1";
>>          [...]
>> };
>>
>> /* Define the basic bus attributes */
>> soft-i2c@1 {
>>          #address-cells = <1>;
>>          #size-cells = <0>;
>>          compatible = "soft-i2c";
>>          clock-frequency = <50000>;
>>
>>          /* Define the proper GPIO pins */
>>          clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
>>          data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
>>
>>          /* Optionally, define some driver node (bus child) */
>>          somedev@0x44 {
>>                  compatible = "somedev";
>>                  reg = <0x44>;
>>                  [...]
>>          };
>> };
>>
>> The device can be accessed by the i2c command:
>>   i2c dev 8                   (bus number set by alias)
>>   i2c probe <0x44>            (address is optionally)
>>   i2c md 0x44 0x0             (dump dev registers at address 0x0)
>>   Valid chip addresses: 0x44  (success!)
>>   ...
>>
>> The previous driver functionality stays unchanged. Driving the bus lines
>> is done by dm gpio calls in the preprocessor macros. Each, can be redefined
>> by the user as previous.
>>
>> Tested on Trats2 and Odroid U3.
>
> Is the intention to retire the old (non-driver-model) code? What boards use it?

I think not ... Hmm ... maybe you move the new code into
a new file?

>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> Cc: Masahiro Yamada <yamada.m@jp.panasonic.com>
>> Cc: Mike Frysinger <vapier@gentoo.org>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Heiko Schocher <hs@denx.de>

Hups... seems I missed this patches ...

>> ---
>>   drivers/i2c/Makefile   |   1 +
>>   drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++--
>>   2 files changed, 400 insertions(+), 11 deletions(-)

Thanks for this work!

>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index 774bc94..7039b6d 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -6,6 +6,7 @@
>>   #
>>   obj-$(CONFIG_DM_I2C) += i2c-uclass.o
>>   obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
>> +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
>>
>>   obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
>>   obj-$(CONFIG_I2C_MV) += mv_i2c.o
>> diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
>> index db9b402..7afae0b 100644
>> --- a/drivers/i2c/soft_i2c.c
>> +++ b/drivers/i2c/soft_i2c.c
>> @@ -1,4 +1,7 @@
>>   /*
>> + * (C) Copyright 2015, Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    * (C) Copyright 2009
>>    * Heiko Schocher, DENX Software Engineering, hs@denx.de.
>>    * Changes for multibus/multiadapter I2C support.
>> @@ -14,6 +17,11 @@
>>    */
>>
>>   #include <common.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <i2c.h>
>> +#include <div64.h>
>> +#include <asm/gpio.h>
>>   #ifdef CONFIG_MPC8260                  /* only valid for MPC8260 */
>>   #include <ioports.h>
>>   #include <asm/io.h>
>> @@ -32,11 +40,9 @@
>>   #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
>>   #include <asm/io.h>
>>   #endif
>> -#include <i2c.h>
>>
>> +#if defined(CONFIG_SYS_I2C)
>>   #if defined(CONFIG_SOFT_I2C_GPIO_SCL)
>> -# include <asm/gpio.h>
>> -
>>   # ifndef I2C_GPIO_SYNC
>>   #  define I2C_GPIO_SYNC
>>   # endif
>> @@ -85,6 +91,7 @@
>>   # endif
>>
>>   #endif
>> +#endif
>>
>>   /* #define     DEBUG_I2C       */
>>
>> @@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR;
>>   #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE
>>   #endif
>>
>> +/* DM SOFT I2C */
>> +#ifdef CONFIG_DM_I2C_SOFT
>> +# ifndef I2C_GPIO_SYNC
>> +#  define I2C_GPIO_SYNC(gpio)
>> +# endif
>> +
>> +# ifndef I2C_INIT
>> +#  define I2C_INIT(scl, sda)  \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_ACTIVE
>> +#  define I2C_ACTIVE(sda) \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_TRISTATE
>> +#  define I2C_TRISTATE(sda) \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_READ
>> +#  define I2C_READ(sda) dm_gpio_get_value(sda);
>> +# endif
>> +
>> +# ifndef I2C_SDA
>> +#  define I2C_SDA(sda, bit) \
>> +       do { \
>> +               if (bit) { \
>> +                       sda->flags &= ~GPIOD_IS_OUT; \
>> +                       sda->flags |= GPIOD_IS_IN; \
>> +                       dm_gpio_set_dir(sda); \
>> +               } else { \
>> +                       sda->flags &= ~GPIOD_IS_IN; \
>> +                       sda->flags |= GPIOD_IS_OUT; \
>> +                       dm_gpio_set_dir(sda); \
>> +                       dm_gpio_set_value(sda, 0); \
>> +               } \
>> +               I2C_GPIO_SYNC(sda); \
>> +       } while (0)
>> +# endif
>> +
>> +# ifndef I2C_SCL
>> +#  define I2C_SCL(scl, bit) \
>> +       do { \
>> +               scl->flags &= ~GPIOD_IS_IN; \
>> +               scl->flags |= GPIOD_IS_OUT; \
>> +               dm_gpio_set_dir(scl); \
>> +               dm_gpio_set_value(scl, bit); \
>> +               I2C_GPIO_SYNC(scl); \
>> +       } while (0)
>> +# endif
>> +
>> +# ifndef I2C_DELAY
>> +#  define I2C_DELAY(us) udelay(us)     /* 1/4 I2C clock duration */
>> +# endif
>> +
>> +#endif /* CONFIG_DM_I2C_SOFT */
>> +
>>   /*-----------------------------------------------------------------------
>>    * Definitions
>>    */
>> @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR;
>>   #define I2C_ACK                0               /* PD_SDA level to ack a byte */
>>   #define I2C_NOACK      1               /* PD_SDA level to noack a byte */
>>
>> -
>>   #ifdef DEBUG_I2C
>>   #define PRINTD(fmt,args...)    do {    \
>>                  printf (fmt ,##args);   \
>> @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR;
>>   /*-----------------------------------------------------------------------
>>    * Local functions
>>    */
>> +#ifdef CONFIG_SYS_I2C
>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>> -static void  send_reset        (void);
>> +static void send_reset(void);
>> +#endif
>> +static void send_start(void);
>> +static void send_stop(void);
>> +static void send_ack(int);
>> +static int write_byte(uchar byte);
>> +static uchar read_byte(int);
>> +#else
>> +static void send_reset(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_start(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_stop(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int);
>> +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar);
>> +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int);
>>   #endif
>> -static void  send_start        (void);
>> -static void  send_stop (void);
>> -static void  send_ack  (int);
>> -static int   write_byte        (uchar byte);
>> -static uchar read_byte (int);
>> -
>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>>   /*-----------------------------------------------------------------------
>>    * Send a reset sequence consisting of 9 clocks with the data signal high
>>    * to clock any confused device back into an idle state.  Also send a
>>    * <stop> at the end of the sequence for belts & suspenders.
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void send_reset(void)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -166,11 +240,36 @@ static void send_reset(void)
>>          send_stop();
>>          I2C_TRISTATE;
>>   }
>> +#else
>> +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +       int j;
>> +
>> +       I2C_SCL(scl, 1);
>> +       I2C_SDA(sda, 1);
>> +#ifdef I2C_INIT
>> +       I2C_INIT(scl, sda);
>> +#endif
>> +       I2C_TRISTATE(sda);
>> +       for (j = 0; j < 9; j++) {
>> +               I2C_SCL(scl, 0);
>> +               I2C_DELAY(delay);
>> +               I2C_DELAY(delay);
>> +               I2C_SCL(scl, 1);
>> +               I2C_DELAY(delay);
>> +               I2C_DELAY(delay);
>> +       }
>> +       send_stop(scl, sda, delay);
>> +       I2C_TRISTATE(sda);
>
> For the new code I would much prefer that these become functions
> rather than macros. What is the benefit of using a macro?

I do not know the real reason, but I think it comes from powerpc
times, where we had not yet such powerful SoCs and it saved some
instructions ... I tend to say, lets move the dm part into a new
file ... and let it us do like linux ...

@Simon: Do you pick up this patches through DM tree, or should
I pick them up?

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

On 03/24/2015 12:38 AM, Simon Glass wrote:
> Hi Przemyslaw,
>
> On 10 March 2015 at 04:30, Przemyslaw Marczak <p.marczak@samsung.com> wrote:
>> This change adds driver model support to software emulated
>> i2c bus driver. To bind the driver, proper device-tree node
>> must be defined, with the following attributes:
>> - alias: to keep proper bus order
>> - compatible: 'soft-i2c'
>> - clock-frequency [Hz]
>> - clock-pin, data-pin: gpio phandles
>>
>> /* Define the alias number to keep bus numbering order */
>> aliases {
>>          [...]
>>          i2c5 = "/i2c@13500000";
>>          i2c6 = "/soft-i2c@1";
>>          [...]
>> };
>>
>> /* Define the basic bus attributes */
>> soft-i2c@1 {
>>          #address-cells = <1>;
>>          #size-cells = <0>;
>>          compatible = "soft-i2c";
>>          clock-frequency = <50000>;
>>
>>          /* Define the proper GPIO pins */
>>          clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
>>          data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
>>
>>          /* Optionally, define some driver node (bus child) */
>>          somedev@0x44 {
>>                  compatible = "somedev";
>>                  reg = <0x44>;
>>                  [...]
>>          };
>> };
>>
>> The device can be accessed by the i2c command:
>>   i2c dev 8                   (bus number set by alias)
>>   i2c probe <0x44>            (address is optionally)
>>   i2c md 0x44 0x0             (dump dev registers at address 0x0)
>>   Valid chip addresses: 0x44  (success!)
>>   ...
>>
>> The previous driver functionality stays unchanged. Driving the bus lines
>> is done by dm gpio calls in the preprocessor macros. Each, can be redefined
>> by the user as previous.
>>
>> Tested on Trats2 and Odroid U3.
>
> Is the intention to retire the old (non-driver-model) code? What boards use it?
>

Yes, it is in the intention. There is about 20 boards, with defined 
soft-i2c macros in their configs.

>>
>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>> Cc: Masahiro Yamada <yamada.m@jp.panasonic.com>
>> Cc: Mike Frysinger <vapier@gentoo.org>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Heiko Schocher <hs@denx.de>
>> ---
>>   drivers/i2c/Makefile   |   1 +
>>   drivers/i2c/soft_i2c.c | 410 +++++++++++++++++++++++++++++++++++++++++++++++--
>>   2 files changed, 400 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index 774bc94..7039b6d 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -6,6 +6,7 @@
>>   #
>>   obj-$(CONFIG_DM_I2C) += i2c-uclass.o
>>   obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
>> +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
>>
>>   obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
>>   obj-$(CONFIG_I2C_MV) += mv_i2c.o
>> diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
>> index db9b402..7afae0b 100644
>> --- a/drivers/i2c/soft_i2c.c
>> +++ b/drivers/i2c/soft_i2c.c
>> @@ -1,4 +1,7 @@
>>   /*
>> + * (C) Copyright 2015, Samsung Electronics
>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>> + *
>>    * (C) Copyright 2009
>>    * Heiko Schocher, DENX Software Engineering, hs@denx.de.
>>    * Changes for multibus/multiadapter I2C support.
>> @@ -14,6 +17,11 @@
>>    */
>>
>>   #include <common.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <i2c.h>
>> +#include <div64.h>
>> +#include <asm/gpio.h>
>>   #ifdef CONFIG_MPC8260                  /* only valid for MPC8260 */
>>   #include <ioports.h>
>>   #include <asm/io.h>
>> @@ -32,11 +40,9 @@
>>   #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
>>   #include <asm/io.h>
>>   #endif
>> -#include <i2c.h>
>>
>> +#if defined(CONFIG_SYS_I2C)
>>   #if defined(CONFIG_SOFT_I2C_GPIO_SCL)
>> -# include <asm/gpio.h>
>> -
>>   # ifndef I2C_GPIO_SYNC
>>   #  define I2C_GPIO_SYNC
>>   # endif
>> @@ -85,6 +91,7 @@
>>   # endif
>>
>>   #endif
>> +#endif
>>
>>   /* #define     DEBUG_I2C       */
>>
>> @@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR;
>>   #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE
>>   #endif
>>
>> +/* DM SOFT I2C */
>> +#ifdef CONFIG_DM_I2C_SOFT
>> +# ifndef I2C_GPIO_SYNC
>> +#  define I2C_GPIO_SYNC(gpio)
>> +# endif
>> +
>> +# ifndef I2C_INIT
>> +#  define I2C_INIT(scl, sda)  \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_ACTIVE
>> +#  define I2C_ACTIVE(sda) \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_TRISTATE
>> +#  define I2C_TRISTATE(sda) \
>> +       do { } while (0)
>> +# endif
>> +
>> +# ifndef I2C_READ
>> +#  define I2C_READ(sda) dm_gpio_get_value(sda);
>> +# endif
>> +
>> +# ifndef I2C_SDA
>> +#  define I2C_SDA(sda, bit) \
>> +       do { \
>> +               if (bit) { \
>> +                       sda->flags &= ~GPIOD_IS_OUT; \
>> +                       sda->flags |= GPIOD_IS_IN; \
>> +                       dm_gpio_set_dir(sda); \
>> +               } else { \
>> +                       sda->flags &= ~GPIOD_IS_IN; \
>> +                       sda->flags |= GPIOD_IS_OUT; \
>> +                       dm_gpio_set_dir(sda); \
>> +                       dm_gpio_set_value(sda, 0); \
>> +               } \
>> +               I2C_GPIO_SYNC(sda); \
>> +       } while (0)
>> +# endif
>> +
>> +# ifndef I2C_SCL
>> +#  define I2C_SCL(scl, bit) \
>> +       do { \
>> +               scl->flags &= ~GPIOD_IS_IN; \
>> +               scl->flags |= GPIOD_IS_OUT; \
>> +               dm_gpio_set_dir(scl); \
>> +               dm_gpio_set_value(scl, bit); \
>> +               I2C_GPIO_SYNC(scl); \
>> +       } while (0)
>> +# endif
>> +
>> +# ifndef I2C_DELAY
>> +#  define I2C_DELAY(us) udelay(us)     /* 1/4 I2C clock duration */
>> +# endif
>> +
>> +#endif /* CONFIG_DM_I2C_SOFT */
>> +
>>   /*-----------------------------------------------------------------------
>>    * Definitions
>>    */
>> @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR;
>>   #define I2C_ACK                0               /* PD_SDA level to ack a byte */
>>   #define I2C_NOACK      1               /* PD_SDA level to noack a byte */
>>
>> -
>>   #ifdef DEBUG_I2C
>>   #define PRINTD(fmt,args...)    do {    \
>>                  printf (fmt ,##args);   \
>> @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR;
>>   /*-----------------------------------------------------------------------
>>    * Local functions
>>    */
>> +#ifdef CONFIG_SYS_I2C
>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>> -static void  send_reset        (void);
>> +static void send_reset(void);
>> +#endif
>> +static void send_start(void);
>> +static void send_stop(void);
>> +static void send_ack(int);
>> +static int write_byte(uchar byte);
>> +static uchar read_byte(int);
>> +#else
>> +static void send_reset(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_start(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_stop(struct gpio_desc *, struct gpio_desc *, int);
>> +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int);
>> +static int write_byte(struct gpio_desc *, struct gpio_desc *, int, uchar);
>> +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int, int);
>>   #endif
>> -static void  send_start        (void);
>> -static void  send_stop (void);
>> -static void  send_ack  (int);
>> -static int   write_byte        (uchar byte);
>> -static uchar read_byte (int);
>> -
>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>>   /*-----------------------------------------------------------------------
>>    * Send a reset sequence consisting of 9 clocks with the data signal high
>>    * to clock any confused device back into an idle state.  Also send a
>>    * <stop> at the end of the sequence for belts & suspenders.
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void send_reset(void)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -166,11 +240,36 @@ static void send_reset(void)
>>          send_stop();
>>          I2C_TRISTATE;
>>   }
>> +#else
>> +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +       int j;
>> +
>> +       I2C_SCL(scl, 1);
>> +       I2C_SDA(sda, 1);
>> +#ifdef I2C_INIT
>> +       I2C_INIT(scl, sda);
>> +#endif
>> +       I2C_TRISTATE(sda);
>> +       for (j = 0; j < 9; j++) {
>> +               I2C_SCL(scl, 0);
>> +               I2C_DELAY(delay);
>> +               I2C_DELAY(delay);
>> +               I2C_SCL(scl, 1);
>> +               I2C_DELAY(delay);
>> +               I2C_DELAY(delay);
>> +       }
>> +       send_stop(scl, sda, delay);
>> +       I2C_TRISTATE(sda);
>
> For the new code I would much prefer that these become functions
> rather than macros. What is the benefit of using a macro?
>

Right, there are probably no benefits of keeping old code style.

>> +}
>> +#endif /* CONFIG_SYS_I2C_SOFT */
>>   #endif
>>
>>   /*-----------------------------------------------------------------------
>>    * START: High -> Low on SDA while SCL is High
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void send_start(void)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -184,10 +283,25 @@ static void send_start(void)
>>          I2C_SDA(0);
>>          I2C_DELAY;
>>   }
>> +#else
>> +static void send_start(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>>
>> +       I2C_DELAY(delay);
>> +       I2C_SDA(sda, 1);
>> +       I2C_ACTIVE(sda);
>> +       I2C_DELAY(delay);
>> +       I2C_SCL(scl, 1);
>> +       I2C_DELAY(delay);
>> +       I2C_SDA(sda, 0);
>> +       I2C_DELAY(delay);
>> +}
>> +#endif /* CONFIG_SYS_I2C_SOFT */
>>   /*-----------------------------------------------------------------------
>>    * STOP: Low -> High on SDA while SCL is High
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void send_stop(void)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -203,10 +317,28 @@ static void send_stop(void)
>>          I2C_DELAY;
>>          I2C_TRISTATE;
>>   }
>> +#else
>> +static void send_stop(struct gpio_desc *scl, struct gpio_desc *sda, int delay)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +
>> +       I2C_SCL(scl, 0);
>> +       I2C_DELAY(delay);
>> +       I2C_SDA(sda, 0);
>> +       I2C_ACTIVE(sda);
>> +       I2C_DELAY(delay);
>> +       I2C_SCL(scl, 1);
>> +       I2C_DELAY(delay);
>> +       I2C_SDA(sda, 1);
>> +       I2C_DELAY(delay);
>> +       I2C_TRISTATE(sda);
>> +}
>> +#endif /* CONFIG_SYS_I2C_SOFT */
>>
>>   /*-----------------------------------------------------------------------
>>    * ack should be I2C_ACK or I2C_NOACK
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void send_ack(int ack)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -222,10 +354,29 @@ static void send_ack(int ack)
>>          I2C_SCL(0);
>>          I2C_DELAY;
>>   }
>> +#else
>> +static void send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
>> +                    int delay, int ack)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +
>> +       I2C_SCL(scl, 0);
>> +       I2C_DELAY(delay);
>> +       I2C_ACTIVE(sda);
>> +       I2C_SDA(sda, ack);
>> +       I2C_DELAY(delay);
>> +       I2C_SCL(scl, 1);
>> +       I2C_DELAY(delay);
>> +       I2C_DELAY(delay);
>> +       I2C_SCL(scl, 0);
>> +       I2C_DELAY(delay);
>> +}
>> +#endif
>>
>>   /*-----------------------------------------------------------------------
>>    * Send 8 bits and look for an acknowledgement.
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static int write_byte(uchar data)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -263,11 +414,52 @@ static int write_byte(uchar data)
>>
>>          return(nack);   /* not a nack is an ack */
>>   }
>> +#else
>> +static int write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
>> +                     int delay, uchar data)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +       int j;
>> +       int nack;
>> +
>> +       I2C_ACTIVE(sda);
>> +       for (j = 0; j < 8; j++) {
>> +               I2C_SCL(scl, 0);
>> +               I2C_DELAY(delay);
>> +               I2C_SDA(sda, data & 0x80);
>> +               I2C_DELAY(delay);
>> +               I2C_SCL(scl, 1);
>> +               I2C_DELAY(delay);
>> +               I2C_DELAY(delay);
>> +
>> +               data <<= 1;
>> +       }
>> +
>> +       /*
>> +        * Look for an <ACK>(negative logic) and return it.
>> +        */
>> +       I2C_SCL(scl, 0);
>> +       I2C_DELAY(delay);
>> +       I2C_SDA(sda, 1);
>> +       I2C_TRISTATE(sda);
>> +       I2C_DELAY(delay);
>> +       I2C_SCL(scl, 1);
>> +       I2C_DELAY(delay);
>> +       I2C_DELAY(delay);
>> +       nack = I2C_READ(sda);
>> +       I2C_SCL(scl, 0);
>> +       I2C_DELAY(delay);
>> +       I2C_ACTIVE(sda);
>> +
>> +       return nack;    /* not a nack is an ack */
>> +}
>> +#endif
>>
>>   /*-----------------------------------------------------------------------
>>    * if ack == I2C_ACK, ACK the byte so can continue reading, else
>>    * send I2C_NOACK to end the read.
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static uchar read_byte(int ack)
>>   {
>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> @@ -293,10 +485,38 @@ static uchar read_byte(int ack)
>>
>>          return(data);
>>   }
>> +#else
>> +static uchar read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
>> +                      int delay, int ack)
>> +{
>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>> +       int  data;
>> +       int  j;
>> +
>> +       /*
>> +        * Read 8 bits, MSB first.
>> +        */
>> +       I2C_TRISTATE(sda);
>> +       I2C_SDA(sda, 1);
>> +       data = 0;
>> +       for (j = 0; j < 8; j++) {
>> +               I2C_SCL(scl, 0);
>> +               I2C_DELAY(delay);
>> +               I2C_SCL(scl, 1);
>> +               I2C_DELAY(delay);
>> +               data <<= 1;
>> +               data |= I2C_READ(sda);
>> +               I2C_DELAY(delay);
>> +       }
>> +       send_ack(scl, sda, delay, ack);
>>
>> +       return data;
>> +}
>> +#endif /* CONFIG_SYS_I2C_SOFT */
>>   /*-----------------------------------------------------------------------
>>    * Initialization
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>>   {
>>   #if defined(CONFIG_SYS_I2C_INIT_BOARD)
>> @@ -314,12 +534,14 @@ static void soft_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
>>          send_reset ();
>>   #endif
>>   }
>> +#endif
>>
>>   /*-----------------------------------------------------------------------
>>    * Probe to see if a chip is present.  Also good for checking for the
>>    * completion of EEPROM writes since the chip stops responding until
>>    * the write completes (typically 10mSec).
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
>>   {
>>          int rc;
>> @@ -334,10 +556,12 @@ static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t addr)
>>
>>          return (rc ? 1 : 0);
>>   }
>> +#endif
>>
>>   /*-----------------------------------------------------------------------
>>    * Read bytes
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
>>                          int alen, uchar *buffer, int len)
>>   {
>> @@ -409,10 +633,12 @@ static int  soft_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
>>          send_stop();
>>          return(0);
>>   }
>> +#endif
>>
>>   /*-----------------------------------------------------------------------
>>    * Write bytes
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
>>                          int alen, uchar *buffer, int len)
>>   {
>> @@ -444,10 +670,12 @@ static int  soft_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
>>          send_stop();
>>          return(failures);
>>   }
>> +#endif
>>
>>   /*
>>    * Register soft i2c adapters
>>    */
>> +#ifdef CONFIG_SYS_I2C_SOFT
>>   U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe,
>>                           soft_i2c_read, soft_i2c_write, NULL,
>>                           CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE,
>> @@ -473,3 +701,163 @@ U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe,
>>                           CONFIG_SYS_I2C_SOFT_SLAVE_4,
>>                           3)
>>   #endif
>> +#endif /* CONFIG_SYS_I2C_SOFT */
>> +
>> +#ifdef CONFIG_DM_I2C_SOFT
>> +struct soft_i2c_bus {
>> +       unsigned int speed;
>> +       unsigned long delay;
>> +       struct gpio_desc scl;
>> +       struct gpio_desc sda;
>> +};
>> +
>> +static int i2c_write_data(struct soft_i2c_bus *i2c_bus, uchar chip,
>> +                         uchar *buffer, int len, bool end_with_repeated_start)
>> +{
>> +       struct gpio_desc *scl = &i2c_bus->scl;
>> +       struct gpio_desc *sda = &i2c_bus->sda;
>> +       unsigned int delay = i2c_bus->delay;
>> +       int failures = 0;
>> +
>> +       PRINTD("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
>> +
>> +       send_start(scl, sda, delay);
>> +       if (write_byte(scl, sda, delay, chip << 1)) {
>> +               send_stop(scl, sda, delay);
>> +               PRINTD("i2c_write, no chip responded %02X\n", chip);
>> +               return -ENODEV;
>
> -EIO I think
>

Ok, will fix.

>> +       }
>> +
>> +       while (len-- > 0) {
>> +               if (write_byte(scl, sda, delay, *buffer++))
>> +                       failures++;
>> +       }
>> +
>> +       send_stop(scl, sda, delay);
>> +
>> +       return failures;
>> +}
>> +
>> +static int i2c_read_data(struct soft_i2c_bus *i2c_bus, uchar chip,
>> +                        uchar *buffer, int len)
>> +{
>> +       struct gpio_desc *scl = &i2c_bus->scl;
>> +       struct gpio_desc *sda = &i2c_bus->sda;
>> +       unsigned int delay = i2c_bus->delay;
>> +
>> +       PRINTD("%s: chip %x buffer: %x len %d\n", __func__, chip, buffer, len);
>> +
>> +       send_start(scl, sda, delay);
>> +       write_byte(scl, sda, delay, (chip << 1) | 1);   /* read cycle */
>> +
>> +       while (len-- > 0)
>> +               *buffer++ = read_byte(scl, sda, delay, len == 0);
>> +
>> +       send_stop(scl, sda, delay);
>> +
>> +       return 0;
>> +}
>> +
>> +static int soft_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
>> +{
>> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
>> +       int ret;
>> +
>> +       for (; nmsgs > 0; nmsgs--, msg++) {
>> +               bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
>> +               if (msg->flags & I2C_M_RD) {
>> +                       ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
>> +                                           msg->len);
>> +               } else {
>> +                       ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
>> +                                            msg->len, next_is_read);
>> +               }
>> +               if (ret)
>> +                       return -EREMOTEIO;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int soft_i2c_probe(struct udevice *dev, uint chip, uint chip_flags)
>> +{
>> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
>> +       struct gpio_desc *scl = &i2c_bus->scl;
>> +       struct gpio_desc *sda = &i2c_bus->sda;
>> +       unsigned int delay = i2c_bus->delay;
>> +       int ret;
>> +
>> +       send_start(scl, sda, delay);
>> +       ret = write_byte(scl, sda, delay, (chip << 1) | 0);
>> +       send_stop(scl, sda, delay);
>> +
>> +       PRINTD("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
>> +              __func__, dev->seq, dev->name, chip, chip_flags, ret);
>> +
>> +       return ret;
>> +}
>> +
>> +static int soft_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
>> +{
>> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
>> +       struct gpio_desc *scl = &i2c_bus->scl;
>> +       struct gpio_desc *sda = &i2c_bus->sda;
>> +
>> +       i2c_bus->speed = speed;
>> +       i2c_bus->delay = lldiv(1000000, speed << 2);
>> +
>> +       (scl, sda, i2c_bus->delay);
>> +
>> +       PRINTD("%s: bus: %d (%s) speed: %u Hz (1/4 of period: %lu us)\n",
>> +              __func__, dev->seq, dev->name, speed, i2c_bus->delay);
>> +
>> +       return 0;
>> +}
>> +
>> +static int soft_i2c_ofdata_to_platdata(struct udevice *dev)
>> +{
>> +       struct soft_i2c_bus *i2c_bus = dev_get_priv(dev);
>> +       const void *blob = gd->fdt_blob;
>> +       char *pin_name;
>> +       int ret;
>> +
>> +       pin_name = "clock-pin";
>
> nit: You don't need this variable
>
It is used when printing the error, at the end of this function.

>> +       ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
>> +                                        0, &i2c_bus->scl, GPIOD_IS_OUT);
>
> You should not use the nodev version when you have a device. Also below.
>
Ah, right. I made this mistake also in last pmic patches. Will fix for both.

>> +       if (ret)
>> +               goto error;
>> +
>> +       pin_name = "data-pin";
>
> nit: or here
>
>> +       ret = gpio_request_by_name_nodev(blob, dev->of_offset, pin_name,
>> +                                        0, &i2c_bus->sda, GPIOD_IS_OUT);
>> +       if (ret)
>> +               goto error;
>> +
>> +       PRINTD("%s: bus: %d (%s) fdt node ok\n", __func__, dev->seq, dev->name);
>> +
>> +       return 0;
>> +error:
>> +       error("Can't get %s for soft i2c dev: %s", pin_name, dev->name);
>> +       return ret;
>> +}
>> +
>> +static const struct dm_i2c_ops soft_i2c_ops = {
>> +       .xfer           = soft_i2c_xfer,
>> +       .probe_chip     = soft_i2c_probe,
>> +       .set_bus_speed  = soft_i2c_set_bus_speed,
>> +};
>> +
>> +static const struct udevice_id soft_i2c_ids[] = {
>> +       { .compatible = "soft-i2c" },
>> +       { }
>> +};
>> +
>> +U_BOOT_DRIVER(soft_i2c) = {
>> +       .name   = "soft-i2c",
>> +       .id     = UCLASS_I2C,
>> +       .of_match = soft_i2c_ids,
>> +       .ofdata_to_platdata = soft_i2c_ofdata_to_platdata,
>> +       .priv_auto_alloc_size = sizeof(struct soft_i2c_bus),
>> +       .ops    = &soft_i2c_ops,
>> +};
>> +#endif /* CONFIG_DM_I2C_SOFT */
>> --
>> 1.9.1
>>
>
> Regards,
> Simon
>

Best regards,
Przemyslaw Marczak March 26, 2015, 1:18 p.m. UTC | #5
Hello,

On 03/24/2015 07:01 AM, Heiko Schocher wrote:
> Hello Simon, Przemyslaw,
>
> Am 24.03.2015 00:38, schrieb Simon Glass:
>> Hi Przemyslaw,
>>
>> On 10 March 2015 at 04:30, Przemyslaw Marczak <p.marczak@samsung.com>
>> wrote:
>>> This change adds driver model support to software emulated
>>> i2c bus driver. To bind the driver, proper device-tree node
>>> must be defined, with the following attributes:
>>> - alias: to keep proper bus order
>>> - compatible: 'soft-i2c'
>>> - clock-frequency [Hz]
>>> - clock-pin, data-pin: gpio phandles
>>>
>>> /* Define the alias number to keep bus numbering order */
>>> aliases {
>>>          [...]
>>>          i2c5 = "/i2c@13500000";
>>>          i2c6 = "/soft-i2c@1";
>>>          [...]
>>> };
>>>
>>> /* Define the basic bus attributes */
>>> soft-i2c@1 {
>>>          #address-cells = <1>;
>>>          #size-cells = <0>;
>>>          compatible = "soft-i2c";
>>>          clock-frequency = <50000>;
>>>
>>>          /* Define the proper GPIO pins */
>>>          clock-pin = <&gpa0 0 GPIO_ACTIVE_HIGH>;
>>>          data-pin = <&gpa0 1 GPIO_ACTIVE_HIGH>;
>>>
>>>          /* Optionally, define some driver node (bus child) */
>>>          somedev@0x44 {
>>>                  compatible = "somedev";
>>>                  reg = <0x44>;
>>>                  [...]
>>>          };
>>> };
>>>
>>> The device can be accessed by the i2c command:
>>>   i2c dev 8                   (bus number set by alias)
>>>   i2c probe <0x44>            (address is optionally)
>>>   i2c md 0x44 0x0             (dump dev registers at address 0x0)
>>>   Valid chip addresses: 0x44  (success!)
>>>   ...
>>>
>>> The previous driver functionality stays unchanged. Driving the bus lines
>>> is done by dm gpio calls in the preprocessor macros. Each, can be
>>> redefined
>>> by the user as previous.
>>>
>>> Tested on Trats2 and Odroid U3.
>>
>> Is the intention to retire the old (non-driver-model) code? What
>> boards use it?
>
> I think not ... Hmm ... maybe you move the new code into
> a new file?
>
Ok, I will do this for v2.

>>> Signed-off-by: Przemyslaw Marczak <p.marczak@samsung.com>
>>> Cc: Masahiro Yamada <yamada.m@jp.panasonic.com>
>>> Cc: Mike Frysinger <vapier@gentoo.org>
>>> Cc: Simon Glass <sjg@chromium.org>
>>> Cc: Heiko Schocher <hs@denx.de>
>
> Hups... seems I missed this patches ...
>
>>> ---
>>>   drivers/i2c/Makefile   |   1 +
>>>   drivers/i2c/soft_i2c.c | 410
>>> +++++++++++++++++++++++++++++++++++++++++++++++--
>>>   2 files changed, 400 insertions(+), 11 deletions(-)
>
> Thanks for this work!
>
>>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>>> index 774bc94..7039b6d 100644
>>> --- a/drivers/i2c/Makefile
>>> +++ b/drivers/i2c/Makefile
>>> @@ -6,6 +6,7 @@
>>>   #
>>>   obj-$(CONFIG_DM_I2C) += i2c-uclass.o
>>>   obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
>>> +obj-$(CONFIG_DM_I2C_SOFT) += soft_i2c.o
>>>
>>>   obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
>>>   obj-$(CONFIG_I2C_MV) += mv_i2c.o
>>> diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
>>> index db9b402..7afae0b 100644
>>> --- a/drivers/i2c/soft_i2c.c
>>> +++ b/drivers/i2c/soft_i2c.c
>>> @@ -1,4 +1,7 @@
>>>   /*
>>> + * (C) Copyright 2015, Samsung Electronics
>>> + * Przemyslaw Marczak <p.marczak@samsung.com>
>>> + *
>>>    * (C) Copyright 2009
>>>    * Heiko Schocher, DENX Software Engineering, hs@denx.de.
>>>    * Changes for multibus/multiadapter I2C support.
>>> @@ -14,6 +17,11 @@
>>>    */
>>>
>>>   #include <common.h>
>>> +#include <errno.h>
>>> +#include <dm.h>
>>> +#include <i2c.h>
>>> +#include <div64.h>
>>> +#include <asm/gpio.h>
>>>   #ifdef CONFIG_MPC8260                  /* only valid for MPC8260 */
>>>   #include <ioports.h>
>>>   #include <asm/io.h>
>>> @@ -32,11 +40,9 @@
>>>   #if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
>>>   #include <asm/io.h>
>>>   #endif
>>> -#include <i2c.h>
>>>
>>> +#if defined(CONFIG_SYS_I2C)
>>>   #if defined(CONFIG_SOFT_I2C_GPIO_SCL)
>>> -# include <asm/gpio.h>
>>> -
>>>   # ifndef I2C_GPIO_SYNC
>>>   #  define I2C_GPIO_SYNC
>>>   # endif
>>> @@ -85,6 +91,7 @@
>>>   # endif
>>>
>>>   #endif
>>> +#endif
>>>
>>>   /* #define     DEBUG_I2C       */
>>>
>>> @@ -109,6 +116,65 @@ DECLARE_GLOBAL_DATA_PTR;
>>>   #define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE
>>>   #endif
>>>
>>> +/* DM SOFT I2C */
>>> +#ifdef CONFIG_DM_I2C_SOFT
>>> +# ifndef I2C_GPIO_SYNC
>>> +#  define I2C_GPIO_SYNC(gpio)
>>> +# endif
>>> +
>>> +# ifndef I2C_INIT
>>> +#  define I2C_INIT(scl, sda)  \
>>> +       do { } while (0)
>>> +# endif
>>> +
>>> +# ifndef I2C_ACTIVE
>>> +#  define I2C_ACTIVE(sda) \
>>> +       do { } while (0)
>>> +# endif
>>> +
>>> +# ifndef I2C_TRISTATE
>>> +#  define I2C_TRISTATE(sda) \
>>> +       do { } while (0)
>>> +# endif
>>> +
>>> +# ifndef I2C_READ
>>> +#  define I2C_READ(sda) dm_gpio_get_value(sda);
>>> +# endif
>>> +
>>> +# ifndef I2C_SDA
>>> +#  define I2C_SDA(sda, bit) \
>>> +       do { \
>>> +               if (bit) { \
>>> +                       sda->flags &= ~GPIOD_IS_OUT; \
>>> +                       sda->flags |= GPIOD_IS_IN; \
>>> +                       dm_gpio_set_dir(sda); \
>>> +               } else { \
>>> +                       sda->flags &= ~GPIOD_IS_IN; \
>>> +                       sda->flags |= GPIOD_IS_OUT; \
>>> +                       dm_gpio_set_dir(sda); \
>>> +                       dm_gpio_set_value(sda, 0); \
>>> +               } \
>>> +               I2C_GPIO_SYNC(sda); \
>>> +       } while (0)
>>> +# endif
>>> +
>>> +# ifndef I2C_SCL
>>> +#  define I2C_SCL(scl, bit) \
>>> +       do { \
>>> +               scl->flags &= ~GPIOD_IS_IN; \
>>> +               scl->flags |= GPIOD_IS_OUT; \
>>> +               dm_gpio_set_dir(scl); \
>>> +               dm_gpio_set_value(scl, bit); \
>>> +               I2C_GPIO_SYNC(scl); \
>>> +       } while (0)
>>> +# endif
>>> +
>>> +# ifndef I2C_DELAY
>>> +#  define I2C_DELAY(us) udelay(us)     /* 1/4 I2C clock duration */
>>> +# endif
>>> +
>>> +#endif /* CONFIG_DM_I2C_SOFT */
>>> +
>>>
>>> /*-----------------------------------------------------------------------
>>>
>>>    * Definitions
>>>    */
>>> @@ -117,7 +183,6 @@ DECLARE_GLOBAL_DATA_PTR;
>>>   #define I2C_ACK                0               /* PD_SDA level to
>>> ack a byte */
>>>   #define I2C_NOACK      1               /* PD_SDA level to noack a
>>> byte */
>>>
>>> -
>>>   #ifdef DEBUG_I2C
>>>   #define PRINTD(fmt,args...)    do {    \
>>>                  printf (fmt ,##args);   \
>>> @@ -129,21 +194,30 @@ DECLARE_GLOBAL_DATA_PTR;
>>>
>>> /*-----------------------------------------------------------------------
>>>
>>>    * Local functions
>>>    */
>>> +#ifdef CONFIG_SYS_I2C
>>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>>> -static void  send_reset        (void);
>>> +static void send_reset(void);
>>> +#endif
>>> +static void send_start(void);
>>> +static void send_stop(void);
>>> +static void send_ack(int);
>>> +static int write_byte(uchar byte);
>>> +static uchar read_byte(int);
>>> +#else
>>> +static void send_reset(struct gpio_desc *, struct gpio_desc *, int);
>>> +static void send_start(struct gpio_desc *, struct gpio_desc *, int);
>>> +static void send_stop(struct gpio_desc *, struct gpio_desc *, int);
>>> +static void send_ack(struct gpio_desc *, struct gpio_desc *, int, int);
>>> +static int write_byte(struct gpio_desc *, struct gpio_desc *, int,
>>> uchar);
>>> +static uchar read_byte(struct gpio_desc *, struct gpio_desc *, int,
>>> int);
>>>   #endif
>>> -static void  send_start        (void);
>>> -static void  send_stop (void);
>>> -static void  send_ack  (int);
>>> -static int   write_byte        (uchar byte);
>>> -static uchar read_byte (int);
>>> -
>>>   #if !defined(CONFIG_SYS_I2C_INIT_BOARD)
>>>
>>> /*-----------------------------------------------------------------------
>>>
>>>    * Send a reset sequence consisting of 9 clocks with the data
>>> signal high
>>>    * to clock any confused device back into an idle state.  Also send a
>>>    * <stop> at the end of the sequence for belts & suspenders.
>>>    */
>>> +#ifdef CONFIG_SYS_I2C_SOFT
>>>   static void send_reset(void)
>>>   {
>>>          I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>>> @@ -166,11 +240,36 @@ static void send_reset(void)
>>>          send_stop();
>>>          I2C_TRISTATE;
>>>   }
>>> +#else
>>> +static void send_reset(struct gpio_desc *scl, struct gpio_desc *sda,
>>> int delay)
>>> +{
>>> +       I2C_SOFT_DECLARATIONS   /* intentional without ';' */
>>> +       int j;
>>> +
>>> +       I2C_SCL(scl, 1);
>>> +       I2C_SDA(sda, 1);
>>> +#ifdef I2C_INIT
>>> +       I2C_INIT(scl, sda);
>>> +#endif
>>> +       I2C_TRISTATE(sda);
>>> +       for (j = 0; j < 9; j++) {
>>> +               I2C_SCL(scl, 0);
>>> +               I2C_DELAY(delay);
>>> +               I2C_DELAY(delay);
>>> +               I2C_SCL(scl, 1);
>>> +               I2C_DELAY(delay);
>>> +               I2C_DELAY(delay);
>>> +       }
>>> +       send_stop(scl, sda, delay);
>>> +       I2C_TRISTATE(sda);
>>
>> For the new code I would much prefer that these become functions
>> rather than macros. What is the benefit of using a macro?
>
> I do not know the real reason, but I think it comes from powerpc
> times, where we had not yet such powerful SoCs and it saved some
> instructions ... I tend to say, lets move the dm part into a new
> file ... and let it us do like linux ...
>
> @Simon: Do you pick up this patches through DM tree, or should
> I pick them up?
>
> bye,
> Heiko

Yes, I will make the new file.


Best regards,
diff mbox

Patch

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