Patchwork [U-Boot,04/11] powerpc/ppc4xx: Add bitbang i2c interface for gdsys boards

login
register
mail settings
Submitter Dirk Eibach
Date April 22, 2013, 11:16 a.m.
Message ID <1366629372-32022-5-git-send-email-eibach@gdsys.de>
Download mbox | patch
Permalink /patch/238455/
State Superseded
Delegated to: Stefan Roese
Headers show

Comments

Dirk Eibach - April 22, 2013, 11:16 a.m.
This adds an i2c bitbang interface that can coexist with processor i2c.

Signed-off-by: Dirk Eibach <eibach@gdsys.de>
---
 board/gdsys/common/Makefile |    2 +-
 board/gdsys/common/bb_i2c.c |  304 +++++++++++++++++++++++++++++++++++++++++++
 board/gdsys/common/bb_i2c.h |   76 +++++++++++
 3 files changed, 381 insertions(+), 1 deletion(-)
 create mode 100644 board/gdsys/common/bb_i2c.c
 create mode 100644 board/gdsys/common/bb_i2c.h
Heiko Schocher - April 23, 2013, 6:16 a.m.
Hello Stefan,

Am 23.04.2013 08:45, schrieb Stefan Roese:
> Hi Dirk!
> 
> (Added Heiko as I2C custodian to Cc)
> 
> On 22.04.2013 13:16, Dirk Eibach wrote:
>> This adds an i2c bitbang interface that can coexist with processor i2c.

Huh... missed this EMail...

>>
>> Signed-off-by: Dirk Eibach <eibach@gdsys.de>
>> ---
>>  board/gdsys/common/Makefile |    2 +-
>>  board/gdsys/common/bb_i2c.c |  304 +++++++++++++++++++++++++++++++++++++++++++
>>  board/gdsys/common/bb_i2c.h |   76 +++++++++++
>>  3 files changed, 381 insertions(+), 1 deletion(-)
>>  create mode 100644 board/gdsys/common/bb_i2c.c
>>  create mode 100644 board/gdsys/common/bb_i2c.h
> 
> Just checking: Is it really not possible to use multiple different I2C
> interfaces (e.g. PPC4xx I2C and GPIO bitbang I2C)? Heiko, whats the
> current status here?

If this is needed, we should merge the multibus/multiadapter
approach finally to mainline, found here:

http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/20130416_multibus_v2

Yeah, waiting for such a chance for some years ;-)

I am not happy to have boardspecific approaches here! Dirk, could
you give the above sources a try? I must admit that the sources
are just rebased without doing big tests on several plattforms ...
but the branch compiles clean for arm and powerpc. If it works
for you, we can try to bring it in the next merge window in mainline...

- steps to do (for you):
  - port the ppc4xx i2c driver to the new framework
    There is an older branch, where I did this for an older
    version of the multibus/multiadapter approach, see here:
    http://git.denx.de/?p=u-boot/u-boot-i2c.git;a=shortlog;h=refs/heads/multibus_v2_20111112

    It gives you maybe an idea how to do this.

  - for adding a soft i2c driver you should do something like:

+#define CONFIG_SYS_I2C_SOFT            /* I2C bit-banged */
+#define CONFIG_SYS_I2C_SOFT_SPEED      Your speed
+#define CONFIG_SYS_I2C_SOFT_SLAVE      Your slave addr

+#define I2C_TRISTATE   i2c_soft_tristate()
+#define I2C_READ       i2c_soft_read()
+#define I2C_ACTIVE     i2c_soft_delay()
+#define I2C_SCL(bit)   i2c_soft_scl(bit)
+#define I2C_SDA(bit)   i2c_soft_sda(bit)
+#define I2C_DELAY      i2c_soft_delay()
+#define I2C_INIT       i2c_soft_init()

and in your board code define the above functions:

+#if defined(CONFIG_SYS_I2C_SOFT)
+#include <i2c.h>
+
+void i2c_soft_init(void)
+{
+       printf("init: %d\n", I2C_ADAP_HWNR);
+}
+
+void i2c_soft_active(void)
+{
+       printf("active: %d\n", I2C_ADAP_HWNR);
+}
+
+void i2c_soft_tristate(void)
+{
+       printf("tristate: %d\n", I2C_ADAP_HWNR);
+}
+
+int i2c_soft_read(void)
+{
+       return 1;
+}
+
+void i2c_soft_sda(int bit)
+{
+       printf("sda: %d\n", I2C_ADAP_HWNR);
+}
+
+void i2c_soft_scl(int bit)
+{
+       printf("scl: %d\n", I2C_ADAP_HWNR);
+}
+
+void i2c_soft_delay(void)
+{
+       printf("delay: %d\n", I2C_ADAP_HWNR);
+}
+
+#endif

You can have more than one soft i2c adapter by defining
the defines I2C_SOFT_DECLARATIONS2, I2C_SOFT_DECLARATIONS3 ...
and switch between them by interpreting the I2C_ADAP_HWNR
in the above functions ...

bye,
Heiko
Stefan Roese - April 23, 2013, 6:45 a.m.
Hi Dirk!

(Added Heiko as I2C custodian to Cc)

On 22.04.2013 13:16, Dirk Eibach wrote:
> This adds an i2c bitbang interface that can coexist with processor i2c.
> 
> Signed-off-by: Dirk Eibach <eibach@gdsys.de>
> ---
>  board/gdsys/common/Makefile |    2 +-
>  board/gdsys/common/bb_i2c.c |  304 +++++++++++++++++++++++++++++++++++++++++++
>  board/gdsys/common/bb_i2c.h |   76 +++++++++++
>  3 files changed, 381 insertions(+), 1 deletion(-)
>  create mode 100644 board/gdsys/common/bb_i2c.c
>  create mode 100644 board/gdsys/common/bb_i2c.h

Just checking: Is it really not possible to use multiple different I2C
interfaces (e.g. PPC4xx I2C and GPIO bitbang I2C)? Heiko, whats the
current status here?

Thanks,
Stefan
TigerLiu@viatech.com.cn - April 23, 2013, 7:35 a.m.
Hi, experts:
I found an example for burst transaction test in examples\standalone
directory.
I think it's very useful.
But it's for power pc.
Is there an example on ARM platform?

Best wishes,
Wolfgang Denk - April 23, 2013, 12:16 p.m.
Dear TigerLiu@viatech.com.cn,

In message <FE7ADED5C2218B4786C09CD97DC4C49F87C8CC@exchbj02.viatech.com.bj> you wrote:
>
> I found an example for burst transaction test in examples\standalone
> directory.
> I think it's very useful.

Actually it is not.  If you want to stress test a board, boot Linux
with root file system mounted over NFS, and compile a Linux kernel
natively on the target.

> But it's for power pc.
> Is there an example on ARM platform?

None that I know of.  But following the example should be relatively
straight-forward.  Just don't expect any useful results.

Best regards,

Wolfgang Denk

Patch

diff --git a/board/gdsys/common/Makefile b/board/gdsys/common/Makefile
index aa15c15..b217d97 100644
--- a/board/gdsys/common/Makefile
+++ b/board/gdsys/common/Makefile
@@ -31,7 +31,7 @@  LIB	= $(obj)lib$(VENDOR).o
 
 COBJS-$(CONFIG_IO) += miiphybb.o
 COBJS-$(CONFIG_IO64) += miiphybb.o
-COBJS-$(CONFIG_IOCON) += osd.o mclink.o cmd_fpga.o
+COBJS-$(CONFIG_IOCON) += osd.o mclink.o cmd_fpga.o bb_i2c.o
 COBJS-$(CONFIG_DLVISION_10G) += osd.o
 
 COBJS   := $(COBJS-y)
diff --git a/board/gdsys/common/bb_i2c.c b/board/gdsys/common/bb_i2c.c
new file mode 100644
index 0000000..a816514
--- /dev/null
+++ b/board/gdsys/common/bb_i2c.c
@@ -0,0 +1,304 @@ 
+/*
+ * (C) Copyright 2012
+ * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
+ *
+ * based on soft_i2c.c
+ * (C) Copyright 2001, 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * This has been changed substantially by Gerald Van Baren, Custom IDEAS,
+ * vanbaren@cideas.com.  It was heavily influenced by LiMon, written by
+ * Neil Russell.
+ */
+
+#include <common.h>
+
+/*-----------------------------------------------------------------------
+ * Definitions
+ */
+
+#define RETRIES		0
+
+#define BB_I2C_ACK	0		/* PD_SDA level to ack a byte */
+#define BB_I2C_NOACK	1		/* PD_SDA level to noack a byte */
+
+/*-----------------------------------------------------------------------
+ * Local functions
+ */
+static void  send_reset(unsigned int bus);
+static void  send_start(unsigned int bus);
+static void  send_stop(unsigned int bus);
+static void  send_ack(unsigned int bus, int ack);
+static int   write_byte(unsigned int bus, uchar byte);
+static uchar read_byte(unsigned int bus, int ack);
+
+/*-----------------------------------------------------------------------
+ * Send a reset sequence consisting of 9 clocks with the data signal high
+ * to clock any confused device back into an idle state.  Also send a
+ * <stop> at the end of the sequence for belts & suspenders.
+ */
+static void send_reset(unsigned int bus)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+	int j;
+
+	BB_I2C_SCL(bus, 1);
+	BB_I2C_SDA(bus, 1);
+	BB_I2C_TRISTATE(bus);
+	for (j = 0; j < 9; j++) {
+		BB_I2C_SCL(bus, 0);
+		BB_I2C_DELAY(bus);
+		BB_I2C_DELAY(bus);
+		BB_I2C_SCL(bus, 1);
+		BB_I2C_DELAY(bus);
+		BB_I2C_DELAY(bus);
+	}
+	send_stop(bus);
+	BB_I2C_TRISTATE(bus);
+}
+
+/*-----------------------------------------------------------------------
+ * START: High -> Low on SDA while SCL is High
+ */
+static void send_start(unsigned int bus)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+
+	BB_I2C_DELAY(bus);
+	BB_I2C_SDA(bus, 1);
+	BB_I2C_ACTIVE(bus);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SCL(bus, 1);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SDA(bus, 0);
+	BB_I2C_DELAY(bus);
+}
+
+/*-----------------------------------------------------------------------
+ * STOP: Low -> High on SDA while SCL is High
+ */
+static void send_stop(unsigned int bus)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+
+	BB_I2C_SCL(bus, 0);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SDA(bus, 0);
+	BB_I2C_ACTIVE(bus);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SCL(bus, 1);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SDA(bus, 1);
+	BB_I2C_DELAY(bus);
+	BB_I2C_TRISTATE(bus);
+}
+
+/*-----------------------------------------------------------------------
+ * ack should be BB_I2C_ACK or BB_I2C_NOACK
+ */
+static void send_ack(unsigned int bus, int ack)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+
+	BB_I2C_SCL(bus, 0);
+	BB_I2C_DELAY(bus);
+	BB_I2C_ACTIVE(bus);
+	BB_I2C_SDA(bus, ack);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SCL(bus, 1);
+	BB_I2C_DELAY(bus);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SCL(bus, 0);
+	BB_I2C_DELAY(bus);
+}
+
+/*-----------------------------------------------------------------------
+ * Send 8 bits and look for an acknowledgement.
+ */
+static int write_byte(unsigned int bus, uchar data)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+	int j;
+	int nack;
+
+	BB_I2C_ACTIVE(bus);
+	for (j = 0; j < 8; j++) {
+		BB_I2C_SCL(bus, 0);
+		BB_I2C_DELAY(bus);
+		BB_I2C_SDA(bus, data & 0x80);
+		BB_I2C_DELAY(bus);
+		BB_I2C_SCL(bus, 1);
+		BB_I2C_DELAY(bus);
+		BB_I2C_DELAY(bus);
+
+		data <<= 1;
+	}
+
+	/*
+	 * Look for an <ACK>(negative logic) and return it.
+	 */
+	BB_I2C_SCL(bus, 0);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SDA(bus, 1);
+	BB_I2C_TRISTATE(bus);
+	BB_I2C_DELAY(bus);
+	BB_I2C_SCL(bus, 1);
+	BB_I2C_DELAY(bus);
+	BB_I2C_DELAY(bus);
+	nack = BB_I2C_READ(bus);
+	BB_I2C_SCL(bus, 0);
+	BB_I2C_DELAY(bus);
+	BB_I2C_ACTIVE(bus);
+
+	return nack;	/* not a nack is an ack */
+}
+
+/*-----------------------------------------------------------------------
+ * if ack == BB_I2C_ACK, ACK the byte so can continue reading, else
+ * send BB_I2C_NOACK to end the read.
+ */
+static uchar read_byte(unsigned int bus, int ack)
+{
+	BB_I2C_SOFT_DECLARATIONS(bus)	/* intentional without ';' */
+	int  data;
+	int  j;
+
+	/*
+	 * Read 8 bits, MSB first.
+	 */
+	BB_I2C_TRISTATE(bus);
+	BB_I2C_SDA(bus, 1);
+	data = 0;
+	for (j = 0; j < 8; j++) {
+		BB_I2C_SCL(bus, 0);
+		BB_I2C_DELAY(bus);
+		BB_I2C_SCL(bus, 1);
+		BB_I2C_DELAY(bus);
+		data <<= 1;
+		data |= BB_I2C_READ(bus);
+		BB_I2C_DELAY(bus);
+	}
+	send_ack(bus, ack);
+
+	return data;
+}
+
+/*=====================================================================*/
+/*                         Public Functions                            */
+/*=====================================================================*/
+
+/*-----------------------------------------------------------------------
+ * Initialization
+ */
+void bb_i2c_init(unsigned int bus, int speed, int slaveaddr)
+{
+	send_reset(bus);
+}
+
+/*-----------------------------------------------------------------------
+ * 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).
+ */
+int bb_i2c_probe(unsigned int bus, uchar addr)
+{
+	int rc;
+
+	/*
+	 * perform 1 byte write transaction with just address byte
+	 * (fake write)
+	 */
+	send_start(bus);
+	rc = write_byte(bus, (addr << 1) | 0);
+	send_stop(bus);
+
+	return rc ? 1 : 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Read bytes
+ */
+int bb_i2c_read(unsigned int bus, uchar chip, uint addr, int alen,
+		uchar *buffer, int len)
+{
+	int shift;
+
+	/*
+	 * Do the addressing portion of a write cycle to set the
+	 * chip's address pointer.  If the address length is zero,
+	 * don't do the normal write cycle to set the address pointer,
+	 * there is no address pointer in this chip.
+	 */
+	send_start(bus);
+	if (alen > 0) {
+		if (write_byte(bus, chip << 1)) {	/* write cycle */
+			send_stop(bus);
+			return 1;
+		}
+		shift = (alen-1) * 8;
+		while (alen-- > 0) {
+			if (write_byte(bus, addr >> shift))
+				return 1;
+			shift -= 8;
+		}
+
+		send_stop(bus);
+		send_start(bus);
+	}
+	/*
+	 * Send the chip address again, this time for a read cycle.
+	 * Then read the data.  On the last byte, we do a NACK instead
+	 * of an ACK(len == 0) to terminate the read.
+	 */
+	write_byte(bus, (chip << 1) | 1);	/* read cycle */
+	while (len-- > 0)
+		*buffer++ = read_byte(bus, len == 0);
+	send_stop(bus);
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Write bytes
+ */
+int  bb_i2c_write(unsigned int bus, uchar chip, uint addr, int alen,
+		  uchar *buffer, int len)
+{
+	int shift, failures = 0;
+
+	send_start(bus);
+	if (write_byte(bus, chip << 1)) {	/* write cycle */
+		send_stop(bus);
+		return 1;
+	}
+	shift = (alen-1) * 8;
+	while (alen-- > 0) {
+		if (write_byte(bus, addr >> shift))
+			return 1;
+		shift -= 8;
+	}
+
+	while (len-- > 0) {
+		if (write_byte(bus, *buffer++))
+			failures++;
+	}
+	send_stop(bus);
+	return failures;
+}
diff --git a/board/gdsys/common/bb_i2c.h b/board/gdsys/common/bb_i2c.h
new file mode 100644
index 0000000..34f4c0f
--- /dev/null
+++ b/board/gdsys/common/bb_i2c.h
@@ -0,0 +1,76 @@ 
+/*
+ * (C) Copyright 2012
+ * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
+ *
+ * based on i2c.h
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * The original I2C interface was
+ *   (C) 2000 by Paolo Scaffardi (arsenio@tin.it)
+ *   AIRVENT SAM s.p.a - RIMINI(ITALY)
+ * but has been changed substantially.
+ */
+
+#ifndef _BB_I2C_H_
+#define _BB_I2C_H_
+
+/*
+ * Probe the given I2C chip address.  Returns 0 if a chip responded,
+ * not 0 on failure.
+ */
+int bb_i2c_probe(unsigned int bus, uchar chip);
+
+/*
+ * Read/Write interface:
+ *   chip:    I2C chip address, range 0..127
+ *   addr:    Memory (register) address within the chip
+ *   alen:    Number of bytes to use for addr (typically 1, 2 for larger
+ *              memories, 0 for register type devices with only one
+ *              register)
+ *   buffer:  Where to read/write the data
+ *   len:     How many bytes to read/write
+ *
+ *   Returns: 0 on success, not 0 on failure
+ */
+int bb_i2c_read(unsigned int bus, uchar chip, uint addr, int alen,
+		uchar *buffer, int len);
+int bb_i2c_write(unsigned int bus, uchar chip, uint addr, int alen,
+		 uchar *buffer, int len);
+
+/*
+ * Utility routines to read/write registers.
+ */
+static inline u8 bb_i2c_reg_read(unsigned int bus, u8 addr, u8 reg)
+{
+	u8 buf;
+
+	bb_i2c_read(bus, addr, reg, 1, &buf, 1);
+
+	return buf;
+}
+
+static inline void bb_i2c_reg_write(unsigned int bus, u8 addr, u8 reg, u8 val)
+{
+	bb_i2c_write(bus, addr, reg, 1, &val, 1);
+}
+
+#endif	/* _BB_I2C_H_ */