[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