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

Hans de Goede hdegoede at redhat.com
Tue Mar 18 00:00:51 CET 2014


From: Henrik Nordstrom <henrik at 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 at denx.de>

Signed-off-by: Henrik Nordstrom <henrik at henriknordstrom.net>
Signed-off-by: Oliver Schinagl <oliver at schinagl.nl>
Signed-off-by: Hans de Goede <hdegoede at 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/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 at henriknordstrom.net>
+ *
+ * Based on sun4i linux kernle i2c.h
+ * (C) Copyright 2007-2012
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tanglaing at allwinnertech.com>
+ * Victor Wei <weiziheng at 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 at 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
-- 
1.9.0



More information about the U-Boot mailing list