Patch set updated for coreboot: 132fe49 cpu/allwinner/a10: Add basic TWI (I²C) driver

Alexandru Gagniuc (mr.nuke.me@gmail.com) gerrit at coreboot.org
Thu Jan 2 23:29:53 CET 2014


Alexandru Gagniuc (mr.nuke.me at gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/4588

-gerrit

commit 132fe49489b589b1f81f77c583204334b5266138
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
Date:   Sat Dec 28 21:50:54 2013 -0500

    cpu/allwinner/a10: Add basic TWI (I²C) driver
    
    Change-Id: I11b10301199e5ff1a45d9b7d2958cc7b6667a29c
    Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
---
 src/cpu/allwinner/a10/Makefile.inc |   1 +
 src/cpu/allwinner/a10/twi.c        | 206 +++++++++++++++++++++++++++++++++++++
 src/cpu/allwinner/a10/twi.h        |  58 +++++++++++
 3 files changed, 265 insertions(+)

diff --git a/src/cpu/allwinner/a10/Makefile.inc b/src/cpu/allwinner/a10/Makefile.inc
index fecaf0e..6865529 100644
--- a/src/cpu/allwinner/a10/Makefile.inc
+++ b/src/cpu/allwinner/a10/Makefile.inc
@@ -11,6 +11,7 @@ romstage-y	+= bootblock_media.c
 ramstage-y	+= uart.c
 ramstage-y	+= uart_console.c
 ramstage-y	+= timer.c
+ramstage-y	+= twi.c
 ramstage-y	+= monotonic_timer.c
 ramstage-y	+= bootblock_media.c
 
diff --git a/src/cpu/allwinner/a10/twi.c b/src/cpu/allwinner/a10/twi.c
new file mode 100644
index 0000000..692a498
--- /dev/null
+++ b/src/cpu/allwinner/a10/twi.c
@@ -0,0 +1,206 @@
+/*
+ * Setup helpers for Two Wire Interface (TWI) (I²C) Allwinner CPUs
+ *
+ * Only functionality for I²C master is provided.
+ * Largely based on the uboot-sunxi code.
+ *
+ * Copyright (C) 2012 Henrik Nordstrom <henrik at henriknordstrom.net>
+ * Copyright (C) 2013 Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#include "memmap.h"
+#include "twi.h"
+
+#include <arch/io.h>
+#include <delay.h>
+#include <device/i2c.h>
+
+#define TWI_BASE(n)			(A1X_TWI0_BASE + 0x400 * (n))
+
+#define TWI_TIMEOUT			(50 * 1000)
+
+static u8 is_busy(struct a1x_twi *twi)
+{
+	return (read32(&twi->stat) != TWI_STAT_IDLE);
+}
+
+static enum cb_err wait_until_idle(struct a1x_twi *twi)
+{
+	u32 i = TWI_TIMEOUT;
+	while (i-- && is_busy((twi)))
+		udelay(1);
+	return i ? CB_SUCCESS : CB_ERR;
+}
+
+/* FIXME: This function is basic, and unintelligent */
+static void configure_clock(struct a1x_twi *twi, u32 speed_hz)
+{
+	/* FIXME: We assume clock is 24MHz, which may not be the case */
+	u32 apb_clk = 24000000, m, n;
+
+	/* Pre-divide the clock by 8 */
+	n = 3;
+	m = (apb_clk >> n) / speed_hz;
+	write32(TWI_CLK_M(m) | TWI_CLK_N(n), &twi->clk);
+}
+
+void a1x_twi_init(u8 bus, u32 speed_hz)
+{
+	u32 i = TWI_TIMEOUT;
+	struct a1x_twi *twi = (void *)TWI_BASE(bus);
+
+	configure_clock(twi, speed_hz);
+
+	/* Enable the I²C bus */
+	write32(TWI_CTL_BUS_EN, &twi->ctl);
+	/* Issue soft reset */
+	write32(1, &twi->reset);
+
+	while (i-- && read32(&twi->reset))
+		udelay(1);
+}
+
+static void clear_interrupt_flag(struct a1x_twi *twi)
+{
+	write32(read32(&twi->ctl) & ~TWI_CTL_INT_FLAG, &twi->ctl);
+}
+
+static void i2c_send_data(struct a1x_twi *twi, u8 data)
+{
+	write32(data, &twi->data);
+	clear_interrupt_flag(twi);
+}
+
+static enum twi_status wait_for_status(struct a1x_twi *twi)
+{
+	u32 i = TWI_TIMEOUT;
+	/* Wait until interrupt is asserted again */
+	while (i-- && !(read32(&twi->ctl) & TWI_CTL_INT_FLAG))
+		udelay(1);
+	/* A timeout here most likely indicates a bus error */
+	return i ? read32(&twi->stat) : TWI_STAT_BUS_ERROR;
+}
+
+static void i2c_send_start(struct a1x_twi *twi)
+{
+	u32 reg32, i;
+
+	/* Send START condition */
+	reg32 = read32(&twi->ctl);
+	reg32 &= ~TWI_CTL_INT_FLAG;
+	reg32 |= TWI_CTL_M_START;
+	write32(reg32, &twi->ctl);
+
+	/* M_START is automatically cleared after condition is transmitted */
+	i = TWI_TIMEOUT;
+	while (i-- && (read32(&twi->ctl) & TWI_CTL_M_START))
+		udelay(1);
+}
+
+static void i2c_send_stop(struct a1x_twi *twi)
+{
+	u32 reg32;
+
+	/* Send STOP condition */
+	reg32 = read32(&twi->ctl);
+	reg32 &= ~TWI_CTL_INT_FLAG;
+	reg32 |= TWI_CTL_M_STOP;
+	write32(reg32, &twi->ctl);
+}
+
+int i2c_read(unsigned bus, unsigned chip, unsigned addr,
+	     unsigned alen, uint8_t *buf, unsigned len)
+{
+	unsigned count = len;
+	enum twi_status expected_status;
+	struct a1x_twi *twi = (void *)TWI_BASE(bus);
+
+	if (wait_until_idle(twi) != CB_SUCCESS)
+		return CB_ERR;
+
+	i2c_send_start(twi);
+	if (wait_for_status(twi) != TWI_STAT_TX_START)
+		return CB_ERR;
+
+	/* Send chip address */
+	i2c_send_data(twi, chip << 1);
+	if (wait_for_status(twi) != TWI_STAT_TX_AW_ACK)
+		return CB_ERR;
+
+	/* Send data address */
+	i2c_send_data(twi, addr);
+	if (wait_for_status(twi) != TWI_STAT_TXD_ACK)
+		return CB_ERR;
+
+	/* Send restart for read */
+	i2c_send_start(twi);
+	if (wait_for_status(twi) != TWI_STAT_TX_RSTART)
+		return CB_ERR;
+
+	/* Send chip address */
+	i2c_send_data(twi, chip << 1 | 1);
+	if (wait_for_status(twi) != TWI_STAT_TX_AR_ACK)
+		return CB_ERR;
+
+	/* Start ACK-ing received data */
+	setbits_le32(&twi->ctl, TWI_CTL_A_ACK);
+	expected_status = TWI_STAT_RXD_ACK;
+
+	/* Read data */
+	while (count > 0) {
+		if (count == 1) {
+			/* Do not ACK the last byte */
+			clrbits_le32(&twi->ctl, TWI_CTL_A_ACK);
+			expected_status = TWI_STAT_RXD_NAK;
+		}
+
+		clear_interrupt_flag(twi);
+
+		if (wait_for_status(twi) != expected_status)
+			return CB_ERR;
+
+		*buf++ = read32(&twi->data);
+		count--;
+	}
+
+	i2c_send_stop(twi);
+
+	return len;
+}
+
+int i2c_write(unsigned bus, unsigned chip, unsigned addr,
+	      unsigned alen, const uint8_t *buf, unsigned len)
+{
+	unsigned count = len;
+	struct a1x_twi *twi = (void *)TWI_BASE(bus);
+
+	if (wait_until_idle(twi) != CB_SUCCESS)
+		return CB_ERR;
+
+	i2c_send_start(twi);
+	if (wait_for_status(twi) != TWI_STAT_TX_START)
+		return CB_ERR;
+
+	/* Send chip address */
+	i2c_send_data(twi, chip << 1);
+	if (wait_for_status(twi) != TWI_STAT_TX_AW_ACK)
+		return CB_ERR;
+
+	/* Send data address */
+	i2c_send_data(twi, addr);
+	if (wait_for_status(twi) != TWI_STAT_TXD_ACK)
+		return CB_ERR;
+
+	/* Send data */
+	while (count > 0) {
+		i2c_send_data(twi, *buf++);
+		if (wait_for_status(twi) != TWI_STAT_TXD_ACK)
+			return CB_ERR;
+		count--;
+	}
+
+	i2c_send_stop(twi);
+
+	return len;
+}
diff --git a/src/cpu/allwinner/a10/twi.h b/src/cpu/allwinner/a10/twi.h
new file mode 100644
index 0000000..c5f2974
--- /dev/null
+++ b/src/cpu/allwinner/a10/twi.h
@@ -0,0 +1,58 @@
+/*
+ * Definitions Two Wire Interface (TWI) (I²C) Allwinner CPUs
+ *
+ * Copyright (C) 2013  Alexandru Gagniuc <mr.nuke.me at gmail.com>
+ * Subject to the GNU GPL v2, or (at your option) any later version.
+ */
+
+#ifndef CPU_ALLWINNER_A10_TWI_H
+#define CPU_ALLWINNER_A10_TWI_H
+
+#include <types.h>
+
+/* TWI_CTL values */
+#define TWI_CTL_INT_EN		(1 << 7)
+#define TWI_CTL_BUS_EN		(1 << 6)
+#define TWI_CTL_M_START		(1 << 5)
+#define TWI_CTL_M_STOP		(1 << 4)
+#define TWI_CTL_INT_FLAG	(1 << 3)
+#define TWI_CTL_A_ACK	(1 << 2)
+
+/* TWI_STAT values */
+enum twi_status {
+	TWI_STAT_BUS_ERROR 	= 0x00,		/**< Bus error */
+	TWI_STAT_TX_START	= 0x08,		/**< START sent */
+	TWI_STAT_TX_RSTART	= 0x10,		/**< Repeated START sent */
+	TWI_STAT_TX_AW_ACK	= 0x18,		/**< Sent address+read, ACK */
+	TWI_STAT_TX_AW_NAK	= 0x20,		/**< Sent address+read, NAK */
+	TWI_STAT_TXD_ACK	= 0x28,		/**< Sent data, got ACK */
+	TWI_STAT_TXD_NAK	= 0x30,		/**< Sent data, no ACK */
+	TWI_STAT_LOST_ARB	= 0x38,		/**< Lost arbitration */
+	TWI_STAT_TX_AR_ACK	= 0x40,		/**< Sent address+write, ACK */
+	TWI_STAT_TX_AR_NAK	= 0x48,		/**< Sent address+write, NAK */
+	TWI_STAT_RXD_ACK	= 0x50,		/**< Got data, sent ACK */
+	TWI_STAT_RXD_NAK	= 0x58,		/**< Got data, no ACK */
+	TWI_STAT_IDLE		= 0xf8,		/**< Bus idle*/
+};
+
+/* TWI_CLK values */
+#define TWI_CLK_M_MASK		(0xf << 3)
+#define TWI_CLK_M(m)		(((m - 1) << 3) & TWI_CLK_M_MASK)
+#define TWI_CLK_N_MASK		(0x7 << 0)
+#define TWI_CLK_N(n)		(((n) << 3) & TWI_CLK_N_MASK)
+
+struct a1x_twi {
+	u32 addr;	/**< 0x00: Slave address */
+	u32 xaddr;	/**< 0x04: Extended slave address */
+	u32 data;	/**< 0x08: Data byte */
+	u32 ctl;	/**< 0x0C: Control register */
+	u32 stat;	/**< 0x10: Status register */
+	u32 clk;	/**< 0x14: Clock control register */
+	u32 reset;	/**< 0x18: Software reset */
+	u32 efr;	/**< 0x1C: Enhanced Feature register */
+	u32 lcr;	/**< 0x20: Line control register */
+};
+
+void a1x_twi_init(u8 bus, u32 speed_hz);
+
+#endif				/* CPU_ALLWINNER_A10_TWI_H */



More information about the coreboot-gerrit mailing list