diff mbox

[U-Boot,u-boot,sunxi,07/12] sunxi: Add i2c support

Message ID 1395097256-7593-8-git-send-email-hdegoede@redhat.com
State RFC
Delegated to: Albert ARIBAUD
Headers show

Commit Message

Hans de Goede March 17, 2014, 11 p.m. UTC
From: Henrik Nordstrom <henrik@henriknordstrom.net>

Based linux-sunxi#sunxi commit d854c4de2f57 "arm: Handle .gnu.hash section in
ldscripts" vs v2014.01.

As well as the following signed-off-by the sunxi branch shows commits to
the sunxi_i2c.c file by:

Stefan Roese <sr@denx.de>

Signed-off-by: Henrik Nordstrom <henrik@henriknordstrom.net>
Signed-off-by: Oliver Schinagl <oliver@schinagl.nl>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
 arch/arm/cpu/armv7/sunxi/board.c      |   6 +
 arch/arm/include/asm/arch-sunxi/i2c.h | 169 ++++++++++++++++++++++
 drivers/i2c/Makefile                  |   1 +
 drivers/i2c/sunxi_i2c.c               | 260 ++++++++++++++++++++++++++++++++++
 include/configs/sunxi-common.h        |   8 ++
 5 files changed, 444 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-sunxi/i2c.h
 create mode 100644 drivers/i2c/sunxi_i2c.c

Comments

Heiko Schocher March 18, 2014, 7:54 a.m. UTC | #1
Hello Hans,

Am 18.03.2014 00:00, schrieb Hans de Goede:
> From: Henrik Nordstrom<henrik@henriknordstrom.net>
>
> Based linux-sunxi#sunxi commit d854c4de2f57 "arm: Handle .gnu.hash section in
> ldscripts" vs v2014.01.
>
> As well as the following signed-off-by the sunxi branch shows commits to
> the sunxi_i2c.c file by:
>
> Stefan Roese<sr@denx.de>
>
> Signed-off-by: Henrik Nordstrom<henrik@henriknordstrom.net>
> Signed-off-by: Oliver Schinagl<oliver@schinagl.nl>
> Signed-off-by: Hans de Goede<hdegoede@redhat.com>
> ---
>   arch/arm/cpu/armv7/sunxi/board.c      |   6 +
>   arch/arm/include/asm/arch-sunxi/i2c.h | 169 ++++++++++++++++++++++
>   drivers/i2c/Makefile                  |   1 +
>   drivers/i2c/sunxi_i2c.c               | 260 ++++++++++++++++++++++++++++++++++
>   include/configs/sunxi-common.h        |   8 ++
>   5 files changed, 444 insertions(+)
>   create mode 100644 arch/arm/include/asm/arch-sunxi/i2c.h
>   create mode 100644 drivers/i2c/sunxi_i2c.c
>
[...]
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index fa3a875..2a44db4 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -15,6 +15,7 @@ obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
>   obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
>   obj-$(CONFIG_U8500_I2C) += u8500_i2c.o
>   obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
> +obj-$(CONFIG_SUNXI_I2C) += sunxi_i2c.o

please use:

CONFIG_SYS_I2C_SUNXI

>   obj-$(CONFIG_SYS_I2C) += i2c_core.o
>   obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
>   obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
> diff --git a/drivers/i2c/sunxi_i2c.c b/drivers/i2c/sunxi_i2c.c
> new file mode 100644
> index 0000000..9a542f6
> --- /dev/null
> +++ b/drivers/i2c/sunxi_i2c.c
> @@ -0,0 +1,260 @@
> +/*
> + * (C) Copyright 2012 Henrik Nordstrom<henrik@henriknordstrom.net>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include<common.h>
> +#include<i2c.h>
> +#include<asm/io.h>
> +#include<asm/arch/clock.h>
> +#include<asm/arch/cpu.h>
> +#include<asm/arch/gpio.h>
> +#include<asm/arch/i2c.h>
> +
> +static struct i2c __attribute__ ((section(".data"))) *i2c_base =
> +	(struct i2c *)0x1c2ac00;

Please no magic numbers, use a define instead.

> +void i2c_init(int speed, int slaveaddr)
[...]
> +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
> +{
> +	int rc = i2c_do_write(chip, addr, alen, buffer, len);
> +
> +	i2c_stop();
> +
> +	return rc;
> +}

Please update to the new i2c multibus/multiadpater framework

Dummy question, there is another "twi" driver in drivers/i2c

bfin-twi_i2c.c

are they compatible?

> diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
> index fe41b89..1f2bcb5 100644
> --- a/include/configs/sunxi-common.h
> +++ b/include/configs/sunxi-common.h
> @@ -176,6 +176,14 @@
>   #undef CONFIG_CMD_NET
>   #undef CONFIG_CMD_NFS
>
> +/* I2C */
> +#define CONFIG_SPL_I2C_SUPPORT
> +#define CONFIG_SYS_I2C_SPEED		400000
> +#define CONFIG_HARD_I2C

NACK, please use no longer HARD_I2C, switch instead to
CONFIG_SYS_I2C

> +#define CONFIG_SUNXI_I2C
> +#define CONFIG_SYS_I2C_SLAVE		0x7f
> +#define CONFIG_CMD_I2C
> +
>   #ifndef CONFIG_CONS_INDEX
>   #define CONFIG_CONS_INDEX              1       /* UART0 */
>   #endif

bye,
Heiko
Hans de Goede May 3, 2014, 3:46 p.m. UTC | #2
Hi,

On 03/18/2014 08:54 AM, Heiko Schocher wrote:
> Hello Hans,
> 
> Am 18.03.2014 00:00, schrieb Hans de Goede:
>> From: Henrik Nordstrom<henrik@henriknordstrom.net>
>>
>> Based linux-sunxi#sunxi commit d854c4de2f57 "arm: Handle .gnu.hash section in
>> ldscripts" vs v2014.01.
>>
>> As well as the following signed-off-by the sunxi branch shows commits to
>> the sunxi_i2c.c file by:
>>
>> Stefan Roese<sr@denx.de>
>>
>> Signed-off-by: Henrik Nordstrom<henrik@henriknordstrom.net>
>> Signed-off-by: Oliver Schinagl<oliver@schinagl.nl>
>> Signed-off-by: Hans de Goede<hdegoede@redhat.com>
>> ---
>>   arch/arm/cpu/armv7/sunxi/board.c      |   6 +
>>   arch/arm/include/asm/arch-sunxi/i2c.h | 169 ++++++++++++++++++++++
>>   drivers/i2c/Makefile                  |   1 +
>>   drivers/i2c/sunxi_i2c.c               | 260 ++++++++++++++++++++++++++++++++++
>>   include/configs/sunxi-common.h        |   8 ++
>>   5 files changed, 444 insertions(+)
>>   create mode 100644 arch/arm/include/asm/arch-sunxi/i2c.h
>>   create mode 100644 drivers/i2c/sunxi_i2c.c
>>
> [...]
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index fa3a875..2a44db4 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -15,6 +15,7 @@ obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
>>   obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
>>   obj-$(CONFIG_U8500_I2C) += u8500_i2c.o
>>   obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
>> +obj-$(CONFIG_SUNXI_I2C) += sunxi_i2c.o
> 
> please use:
> 
> CONFIG_SYS_I2C_SUNXI

Ok.

> 
>>   obj-$(CONFIG_SYS_I2C) += i2c_core.o
>>   obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
>>   obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
>> diff --git a/drivers/i2c/sunxi_i2c.c b/drivers/i2c/sunxi_i2c.c
>> new file mode 100644
>> index 0000000..9a542f6
>> --- /dev/null
>> +++ b/drivers/i2c/sunxi_i2c.c
>> @@ -0,0 +1,260 @@
>> +/*
>> + * (C) Copyright 2012 Henrik Nordstrom<henrik@henriknordstrom.net>
>> + *
>> + * SPDX-License-Identifier:    GPL-2.0+
>> + */
>> +
>> +#include<common.h>
>> +#include<i2c.h>
>> +#include<asm/io.h>
>> +#include<asm/arch/clock.h>
>> +#include<asm/arch/cpu.h>
>> +#include<asm/arch/gpio.h>
>> +#include<asm/arch/i2c.h>
>> +
>> +static struct i2c __attribute__ ((section(".data"))) *i2c_base =
>> +    (struct i2c *)0x1c2ac00;
> 
> Please no magic numbers, use a define instead.
> 
>> +void i2c_init(int speed, int slaveaddr)
> [...]
>> +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
>> +{
>> +    int rc = i2c_do_write(chip, addr, alen, buffer, len);
>> +
>> +    i2c_stop();
>> +
>> +    return rc;
>> +}
> 
> Please update to the new i2c multibus/multiadpater framework
> 
> Dummy question, there is another "twi" driver in drivers/i2c
> 
> bfin-twi_i2c.c
> 
> are they compatible?

No, but that is a good point, in the kernel we ended up using the
i2c-mv64xxx driver as that is the same controller. I should have thought
of doing the same for u-boot instead of just taking Allwinner's driver.

I've just completed writing a patch-set which uses the mvtwsi driver
instead of introducing a new driver. While working on this I found 2 issues
in the driver, for which I'll be sending patches shortly.

Regards,

Hans
diff mbox

Patch

diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c
index 72eed16..b00bf89 100644
--- a/arch/arm/cpu/armv7/sunxi/board.c
+++ b/arch/arm/cpu/armv7/sunxi/board.c
@@ -11,6 +11,7 @@ 
  */
 
 #include <common.h>
+#include <i2c.h>
 #include <serial.h>
 #ifdef CONFIG_SPL_BUILD
 #include <spl.h>
@@ -94,6 +95,11 @@  void s_init(void)
 	gd = &gdata;
 	preloader_console_init();
 
+#ifdef CONFIG_SPL_I2C_SUPPORT
+	/* Needed early by sunxi_board_init if PMU is enabled */
+	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+#endif
+
 	sunxi_board_init();
 #endif
 }
diff --git a/arch/arm/include/asm/arch-sunxi/i2c.h b/arch/arm/include/asm/arch-sunxi/i2c.h
new file mode 100644
index 0000000..74c2d18
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/i2c.h
@@ -0,0 +1,169 @@ 
+/*
+ * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
+ *
+ * Based on sun4i linux kernle i2c.h
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tanglaing@allwinnertech.com>
+ * Victor Wei <weiziheng@allwinnertech.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#ifndef _SUNXI_I2C_H_
+#define _SUNXI_I2C_H_
+
+struct i2c {
+	u32 saddr;	/*  31:8bit res,7-1bit for slave addr,0 bit for GCE */
+	u32 xsaddr;	/*  31:8bit res,7-0bit for second addr in 10bit addr */
+	u32 data;	/*  31:8bit res, 7-0bit send or receive data byte */
+	u32 ctl;	/*  INT_EN,BUS_EN,M_STA,INT_FLAG,A_ACK */
+	u32 status;	/*  28 interrupt types + 0xf8 normal type = 29  */
+	u32 clkr;	/*  31:7bit res,6-3bit,CLK_M,2-0bit CLK_N */
+	u32 reset;	/*  31:1bit res;0bit,write 1 to clear 0. */
+	u32 efr;	/*  31:2bit res,1:0 bit data byte follow read comand */
+	u32 lctl;	/*  31:6bits res 5:0bit for sda&scl control */
+};
+
+/* TWI address register */
+#define TWI_GCE_EN	(0x1 << 0)	/* gen call addr enable slave mode */
+#define TWI_ADDR_MASK	(0x7f << 1)	/* 7:1bits */
+#define TWI_XADDR_MASK	0xff		/* 7:0bits for extend slave address */
+
+#define TWI_DATA_MASK	0xff		/* 7:0bits for send or received */
+
+/* TWI Control Register Bit Fields */
+/* 1:0 bits reserved */
+/* set 1 to send A_ACK,then low level on SDA */
+#define TWI_CTL_ACK	(0x1 << 2)
+/* INT_FLAG,interrupt status flag: set '1' when interrupt coming */
+#define TWI_CTL_INTFLG	(0x1 << 3)
+#define TWI_CTL_STP	(0x1 << 4)	/* M_STP,Automatic clear 0 */
+#define TWI_CTL_STA	(0x1 << 5)	/* M_STA,atutomatic clear 0 */
+#define TWI_CTL_BUSEN	(0x1 << 6)	/* BUS_EN, mastr mode should be set 1 */
+#define TWI_CTL_INTEN	(0x1 << 7)	/* INT_EN */
+/* 31:8 bit reserved */
+
+/*
+ * TWI Clock Register Bit Fields & Masks,default value:0x0000_0000
+ * Fin is APB CLOCK INPUT;
+ * Fsample = F0 = Fin/2^CLK_N;
+ *           F1 = F0/(CLK_M+1);
+ *
+ * Foscl = F1/10 = Fin/(2^CLK_N * (CLK_M+1)*10);
+ * Foscl is clock SCL;standard mode:100KHz or fast mode:400KHz
+ */
+
+#define TWI_CLK_DIV_M		(0xf << 3)	/* 6:3bit  */
+#define TWI_CLK_DIV_N		(0x7 << 0)	/* 2:0bit */
+#define TWI_CLK_DIV(N, M)	((((N) & 0xf) << 3) | (((M) & 0x7) << 0))
+
+/* TWI Soft Reset Register Bit Fields & Masks  */
+/* write 1 to clear 0, when complete soft reset clear 0 */
+#define TWI_SRST_SRST		(0x1 << 0)
+
+/* TWI Enhance Feature Register Bit Fields & Masks  */
+/* default -- 0x0 */
+/* 00:no,01: 1byte, 10:2 bytes, 11: 3bytes */
+#define TWI_EFR_MASK		(0x3 << 0)
+#define TWI_EFR_WARC_0		(0x0 << 0)
+#define TWI_EFR_WARC_1		(0x1 << 0)
+#define TWI_EFR_WARC_2		(0x2 << 0)
+#define TWI_EFR_WARC_3		(0x3 << 0)
+
+/* twi line control register -default value: 0x0000_003a */
+/* SDA line state control enable ,1:enable;0:disable */
+#define TWI_LCR_SDA_EN		(0x01 << 0)
+/* SDA line state control bit, 1:high level;0:low level */
+#define TWI_LCR_SDA_CTL		(0x01 << 1)
+/* SCL line state control enable ,1:enable;0:disable */
+#define TWI_LCR_SCL_EN		(0x01 << 2)
+/* SCL line state control bit, 1:high level;0:low level */
+#define TWI_LCR_SCL_CTL		(0x01 << 3)
+/* current state of SDA,readonly bit */
+#define TWI_LCR_SDA_STATE_MASK	(0x01 << 4)
+/* current state of SCL,readonly bit */
+#define TWI_LCR_SCL_STATE_MASK	(0x01 << 5)
+/* 31:6bits reserved */
+#define TWI_LCR_IDLE_STATUS	0x3a
+
+/* TWI Status Register Bit Fields & Masks  */
+#define TWI_STAT_MASK		0xff
+/* 7:0 bits use only,default is 0xf8 */
+#define TWI_STAT_BUS_ERR	0x00	/* BUS ERROR */
+
+/* Master mode use only */
+#define TWI_STAT_TX_STA		0x08	/* START condition transmitted */
+/* Repeated START condition transmitted */
+#define TWI_STAT_TX_RESTA	0x10
+/* Address+Write bit transmitted, ACK received */
+#define TWI_STAT_TX_AW_ACK	0x18
+/* Address+Write bit transmitted, ACK not received */
+#define TWI_STAT_TX_AW_NAK	0x20
+/* data byte transmitted in master mode,ack received */
+#define TWI_STAT_TXD_ACK	0x28
+/* data byte transmitted in master mode ,ack not received */
+#define TWI_STAT_TXD_NAK	0x30
+/* arbitration lost in address or data byte */
+#define TWI_STAT_ARBLOST	0x38
+/* Address+Read bit transmitted, ACK received */
+#define TWI_STAT_TX_AR_ACK	0x40
+/* Address+Read bit transmitted, ACK not received */
+#define TWI_STAT_TX_AR_NAK	0x48
+/* Second Address byte + Write bit transmitted, ACK received */
+#define TWI_STAT_TX_2AW_ACK	0xd0
+/* Second Address byte + Write bit transmitted, ACK received */
+#define TWI_STAT_TX_2AW_NAK	0xd8
+/* data byte received in master mode ,ack transmitted */
+#define TWI_STAT_RXD_ACK	0x50
+/* date byte received in master mode,not ack transmitted */
+#define TWI_STAT_RXD_NAK	0x58
+
+/* Slave mode use only */
+/* Slave address+Write bit received, ACK transmitted */
+#define TWI_STAT_RXWS_ACK	0x60
+/*
+ * Arbitration lost in address as master, slave address + Write bit received,
+ * ACK transmitted
+ */
+#define TWI_STAT_ARBLOST_RXWS_ACK 0x68
+/* General Call address received, ACK transmitted */
+#define TWI_STAT_RXGCAS_ACK	0x70
+/*
+ * Arbitration lost in address as master, General Call address received,
+ * ACK transmitted
+ */
+#define TWI_STAT_ARBLOST_RXGCAS_ACK 0x78
+/* Data byte received after slave address received, ACK transmitted */
+#define TWI_STAT_RXDS_ACK	0x80
+/* Data byte received after slave address received, not ACK transmitted */
+#define TWI_STAT_RXDS_NAK	0x88
+/* Data byte received after General Call received, ACK transmitted */
+#define TWI_STAT_RXDGCAS_ACK	0x90
+/* Data byte received after General Call received, not ACK transmitted */
+#define TWI_STAT_RXDGCAS_NAK	0x98
+/* STOP or repeated START condition received in slave  */
+#define TWI_STAT_RXSTPS_RXRESTAS 0xa0
+/* Slave address + Read bit received, ACK transmitted */
+#define TWI_STAT_RXRS_ACK	0xa8
+/*
+ * Arbitration lost in address as master, slave address + Read bit received,
+ * ACK transmitted
+ */
+#define TWI_STAT_ARBLOST_SLAR_ACK 0xb0
+/* Data byte transmitted in slave mode, ACK received */
+#define TWI_STAT_TXDS_ACK	0xb8
+/* Data byte transmitted in slave mode, ACK not received */
+#define TWI_STAT_TXDS_NAK	0xc0
+/* Last byte transmitted in slave mode, ACK received */
+#define TWI_STAT_TXDSL_ACK	0xc8
+
+/* 10bit Address, second part of address */
+/* Second Address byte+Write bit transmitted,ACK received */
+#define TWI_STAT_TX_SAW_ACK	0xd0
+/* Second Address byte+Write bit transmitted,ACK not received */
+#define TWI_STAT_TX_SAW_NAK	0xd8
+
+/* No relevant status infomation,INT_FLAG = 0 */
+#define TWI_STAT_IDLE		0xf8
+
+#endif
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index fa3a875..2a44db4 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -15,6 +15,7 @@  obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
 obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
 obj-$(CONFIG_U8500_I2C) += u8500_i2c.o
 obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
+obj-$(CONFIG_SUNXI_I2C) += sunxi_i2c.o
 obj-$(CONFIG_SYS_I2C) += i2c_core.o
 obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o
 obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o
diff --git a/drivers/i2c/sunxi_i2c.c b/drivers/i2c/sunxi_i2c.c
new file mode 100644
index 0000000..9a542f6
--- /dev/null
+++ b/drivers/i2c/sunxi_i2c.c
@@ -0,0 +1,260 @@ 
+/*
+ * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/i2c.h>
+
+static struct i2c __attribute__ ((section(".data"))) *i2c_base =
+	(struct i2c *)0x1c2ac00;
+
+void i2c_init(int speed, int slaveaddr)
+{
+	int timeout = 0x2ff;
+
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUNXI_GPB0_TWI0);
+	sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUNXI_GPB0_TWI0);
+	clock_twi_onoff(0, 1);
+
+	/* Enable the i2c bus */
+	writel(TWI_CTL_BUSEN, &i2c_base->ctl);
+
+	/* 400KHz operation M=2, N=1, 24MHz APB clock */
+	writel(TWI_CLK_DIV(2, 1), &i2c_base->clkr);
+	writel(TWI_SRST_SRST, &i2c_base->reset);
+
+	while ((readl(&i2c_base->reset) & TWI_SRST_SRST) && timeout--);
+}
+
+int i2c_probe(uchar chip)
+{
+	return -1;
+}
+
+static int i2c_wait_ctl(int mask, int state)
+{
+	int timeout = 0x2ff;
+	int value = state ? mask : 0;
+
+	debug("i2c_wait_ctl(%x == %x), ctl=%x, status=%x\n", mask, value,
+	      i2c_base->ctl, i2c_base->status);
+
+	while (((readl(&i2c_base->ctl) & mask) != value) && timeout-- > 0);
+
+	debug("i2c_wait_ctl(), timeout=%d, ctl=%x, status=%x\n", timeout,
+	      i2c_base->ctl, i2c_base->status);
+
+	if (timeout != 0)
+		return 0;
+	else
+		return -1;
+}
+
+static void i2c_clear_irq(void)
+{
+	writel(readl(&i2c_base->ctl) & ~TWI_CTL_INTFLG, &i2c_base->ctl);
+}
+
+static int i2c_wait_irq(void)
+{
+	return i2c_wait_ctl(TWI_CTL_INTFLG, 1);
+}
+
+static int i2c_wait_status(int status)
+{
+	int timeout = 0x2ff;
+
+	while (readl(&i2c_base->status) != status && timeout-- > 0);
+
+	if (timeout != 0)
+		return 0;
+	else
+		return -1;
+}
+
+static int i2c_wait_irq_status(int status)
+{
+	if (i2c_wait_irq() != 0)
+		return -1;
+
+	if (readl(&i2c_base->status) != status)
+		return -1;
+
+	return 0;
+}
+
+static int i2c_wait_bus_idle(void)
+{
+	int timeout = 0x2ff;
+
+	while (readl(&i2c_base->lctl) != 0x3a && timeout-- > 0);
+
+	if (timeout != 0)
+		return 0;
+	else
+		return -1;
+}
+
+static int i2c_stop(void)
+{
+	u32 ctl;
+
+	ctl = readl(&i2c_base->ctl) & 0xc0;
+	ctl |= TWI_CTL_STP;
+
+	writel(ctl, &i2c_base->ctl);
+
+	/* dummy to delay one I/O operation to make sure it's started */
+	(void)readl(&i2c_base->ctl);
+
+	if (i2c_wait_ctl(TWI_CTL_STP, 0) != 0)
+		return -1;
+	if (i2c_wait_status(TWI_STAT_IDLE))
+		return -1;
+	if (i2c_wait_bus_idle() != 0)
+		return -1;
+
+	return 0;
+}
+
+static int i2c_send_data(u8 data, u8 status)
+{
+	debug("i2c_write(%02x, %x), ctl=%x, status=%x\n", data, status,
+	      i2c_base->ctl, i2c_base->status);
+
+	writel(data, &i2c_base->data);
+	i2c_clear_irq();
+
+	if (i2c_wait_irq_status(status) != 0)
+		return -1;
+
+	return 0;
+}
+
+static int i2c_start(int status)
+{
+	u32 ctl;
+
+	debug("i2c_start(%x), ctl=%x, status=%x\n", status, i2c_base->ctl,
+	      i2c_base->status);
+	/* Check that the controller is idle */
+	if (status == TWI_STAT_TX_STA &&
+	    readl(&i2c_base->status) != TWI_STAT_IDLE) {
+		return -1;
+	}
+
+	writel(0, &i2c_base->efr);
+
+	/* Send start */
+	ctl = readl(&i2c_base->ctl);
+	ctl |= TWI_CTL_STA;	/* Set start bit */
+	ctl &= ~TWI_CTL_INTFLG;	/* Clear int flag */
+	writel(ctl, &i2c_base->ctl);
+
+	if (i2c_wait_ctl(TWI_CTL_STA, 0) != 0)
+		return -1;
+	if (i2c_wait_irq_status(status) != 0)
+		return -1;
+
+	return 0;
+}
+
+int i2c_do_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+	u32 status;
+	u32 ctl;
+
+	if (i2c_start(TWI_STAT_TX_STA) != 0)
+		return -1;
+
+	/* Send chip address */
+	if (i2c_send_data(chip << 1 | 0, TWI_STAT_TX_AW_ACK) != 0)
+		return -1;
+
+	/* Send data address */
+	if (i2c_send_data(addr, TWI_STAT_TXD_ACK) != 0)
+		return -1;
+
+	/* Send restart for read */
+	if (i2c_start(TWI_STAT_TX_RESTA) != 0)
+		return -1;
+
+	/* Send chip address */
+	if (i2c_send_data(chip << 1 | 1, TWI_STAT_TX_AR_ACK) != 0)
+		return -1;
+
+	/* Set ACK mode */
+	ctl = readl(&i2c_base->ctl);
+	ctl |= TWI_CTL_ACK;
+	writel(ctl, &i2c_base->ctl);
+	status = TWI_STAT_RXD_ACK;
+
+	/* Read data */
+	while (len > 0) {
+		if (len == 1) {
+			/* Set NACK mode (last byte) */
+			ctl = readl(&i2c_base->ctl);
+			ctl &= ~TWI_CTL_ACK;
+			writel(ctl, &i2c_base->ctl);
+			status = TWI_STAT_RXD_NAK;
+		}
+
+		i2c_clear_irq();
+		if (i2c_wait_irq_status(status) != 0)
+			return -1;
+
+		*buffer++ = readl(&i2c_base->data);
+		len--;
+	}
+
+	return 0;
+}
+
+int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+	int rc = i2c_do_read(chip, addr, alen, buffer, len);
+
+	i2c_stop();
+
+	return rc;
+}
+
+static int i2c_do_write(uchar chip, uint addr, int alen, uchar *buffer,
+			int len)
+{
+	if (i2c_start(TWI_STAT_TX_STA) != 0)
+		return -1;
+
+	/* Send chip address */
+	if (i2c_send_data(chip << 1 | 0, TWI_STAT_TX_AW_ACK) != 0)
+		return -1;
+
+	/* Send data address */
+	if (i2c_send_data(addr, TWI_STAT_TXD_ACK) != 0)
+		return -1;
+
+	/* Send data */
+	while (len > 0) {
+		if (i2c_send_data(*buffer++, TWI_STAT_TXD_ACK) != 0)
+			return -1;
+		len--;
+	}
+
+	return 0;
+}
+
+int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
+{
+	int rc = i2c_do_write(chip, addr, alen, buffer, len);
+
+	i2c_stop();
+
+	return rc;
+}
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index fe41b89..1f2bcb5 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -176,6 +176,14 @@ 
 #undef CONFIG_CMD_NET
 #undef CONFIG_CMD_NFS
 
+/* I2C */
+#define CONFIG_SPL_I2C_SUPPORT
+#define CONFIG_SYS_I2C_SPEED		400000
+#define CONFIG_HARD_I2C
+#define CONFIG_SUNXI_I2C
+#define CONFIG_SYS_I2C_SLAVE		0x7f
+#define CONFIG_CMD_I2C
+
 #ifndef CONFIG_CONS_INDEX
 #define CONFIG_CONS_INDEX              1       /* UART0 */
 #endif