[U-Boot] [PATCH] i2c: adding driver for pxa27x, pxa3xx, pxa25x

Andrew Ruder andrew.ruder at elecsyscorp.com
Wed Aug 12 20:43:52 CEST 2015


Cc: Marek Vasut <marex at denx.de>
Cc: Heiko Schocher <hs at denx.de>
Signed-off-by: Andrew Ruder <andrew.ruder at elecsyscorp.com>
---

This driver was written before the driver model stuff was really around (or at
least based on a U-Boot version that only had very preliminary support) so
there might be some older conventions in use here.  I have tested on a PXA270
board running upstream master with this driver, however - so to some extent
this all still works fine.  Let me know if things need to change to be
accepted, and I'll try to find some time to update!

 drivers/i2c/Makefile  |   1 +
 drivers/i2c/pxa_i2c.c | 796 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 797 insertions(+)
 create mode 100644 drivers/i2c/pxa_i2c.c

diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 9b45248..cf2c8a8 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_SYS_I2C_MXS) += mxs_i2c.o
 obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
 obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o
 obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o
+obj-$(CONFIG_SYS_I2C_PXA) += pxa_i2c.o
 obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o
 obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o
 obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
diff --git a/drivers/i2c/pxa_i2c.c b/drivers/i2c/pxa_i2c.c
new file mode 100644
index 0000000..859d6cf
--- /dev/null
+++ b/drivers/i2c/pxa_i2c.c
@@ -0,0 +1,796 @@
+/*
+ *  pxa_i2c.c
+ *
+ *  I2C adapter for the PXA I2C bus access.
+ *
+ *  Copyright (C) 2002 Intrinsyc Software Inc.
+ *  Copyright (C) 2004-2005 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2014 Andrew Ruder
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This is a heavily modified version of the PXA I2C driver from the Linux
+ *  kernel 3.15.10.
+ */
+
+#include <common.h>
+#include <compiler.h>
+#include <i2c.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#define MAX_TRANSFER_MS 5000
+#define WAIT_MASTER_MS 64
+#define ABORT_LENGTH_MS 50
+
+/* Set up some defaults for various configurable pieces of this driver
+ * so that most of the time we don't need extra things defined
+ */
+#if !defined(CONFIG_SYS_PXA_STD_I2C_SPEED)
+#define CONFIG_SYS_PXA_STD_I2C_SPEED 100000
+#endif
+
+#if !defined(CONFIG_SYS_PXA_STD_I2C_SLAVE)
+#define CONFIG_SYS_PXA_STD_I2C_SLAVE 1
+#endif
+
+#if !defined(CONFIG_SYS_PXA_PWR_I2C_SPEED)
+#define CONFIG_SYS_PXA_PWR_I2C_SPEED 40000
+#endif
+
+#if !defined(CONFIG_SYS_PXA_PWR_I2C_SLAVE)
+#define CONFIG_SYS_PXA_PWR_I2C_SLAVE 1
+#endif
+
+#if defined(CONFIG_CPU_MONAHANS) && defined(CONFIG_PXA_PWR_I2C)
+#warning Monahans + PWRI2C is unlikely to work without additional testing
+#endif
+
+/* ICR initialize bit values
+*
+*  15. FM       0 (100 Khz operation)
+*  14. UR       0 (No unit reset)
+*  13. SADIE    0 (Disables the unit from interrupting on slave addresses
+*                                       matching its slave address)
+*  12. ALDIE    0 (Disables the unit from interrupt when it loses arbitration
+*                                       in master mode)
+*  11. SSDIE    0 (Disables interrupts from a slave stop detected, in slave mode)
+*  10. BEIE     1 (Enable interrupts from detected bus errors, no ACK sent)
+*  9.  IRFIE    1 (Enable interrupts from full buffer received)
+*  8.  ITEIE    1 (Enables the I2C unit to interrupt when transmit buffer empty)
+*  7.  GCD      1 (Disables i2c unit response to general call messages as a slave)
+*  6.  IUE      0 (Disable unit until we change settings)
+*  5.  SCLE     1 (Enables the i2c clock output for master mode (drives SCL)
+*  4.  MA       0 (Only send stop with the ICR stop bit)
+*  3.  TB       0 (We are not transmitting a byte initially)
+*  2.  ACKNAK   0 (Send an ACK after the unit receives a byte)
+*  1.  STOP     0 (Do not send a STOP)
+*  0.  START    0 (Do not send a START)
+*
+*/
+#define I2C_ICR_INIT	(ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
+
+/* I2C status register init values
+ *
+ * 10. BED      1 (Clear bus error detected)
+ * 9.  SAD      1 (Clear slave address detected)
+ * 7.  IRF      1 (Clear IDBR Receive Full)
+ * 6.  ITE      1 (Clear IDBR Transmit Empty)
+ * 5.  ALD      1 (Clear Arbitration Loss Detected)
+ * 4.  SSD      1 (Clear Slave Stop Detected)
+ */
+#define I2C_ISR_INIT	0x7FF  /* status register init */
+
+#define dev_dbg(dev, fmt, args...) \
+	debug("pxai2c%d: " fmt, (dev)->hwadapnr, ##args)
+#define dev_err(dev, fmt, args...) \
+	printf("pxai2c%d: " fmt, (dev)->hwadapnr, ##args)
+
+enum i2c_code {
+	I2C_SUCCESS = 0,
+	I2C_RETRY,
+	I2C_NAK,
+	I2C_BUS_ERROR,
+	I2C_TIMEOUT,
+	I2C_OTHER_ERROR,
+	I2C_ALL_RETRIES,
+	I2C_CONTINUE
+};
+
+/*
+ * I2C bit definitions
+ */
+
+#define ICR_START	(1 << 0)	   /* start bit */
+#define ICR_STOP	(1 << 1)	   /* stop bit */
+#define ICR_ACKNAK	(1 << 2)	   /* send ACK(0) or NAK(1) */
+#define ICR_TB		(1 << 3)	   /* transfer byte bit */
+#define ICR_MA		(1 << 4)	   /* master abort */
+#define ICR_SCLE	(1 << 5)	   /* master clock enable */
+#define ICR_IUE		(1 << 6)	   /* unit enable */
+#define ICR_GCD		(1 << 7)	   /* general call disable */
+#define ICR_ITEIE	(1 << 8)	   /* enable tx interrupts */
+#define ICR_IRFIE	(1 << 9)	   /* enable rx interrupts */
+#define ICR_BEIE	(1 << 10)	   /* enable bus error ints */
+#define ICR_SSDIE	(1 << 11)	   /* slave STOP detected int enable */
+#define ICR_ALDIE	(1 << 12)	   /* enable arbitration interrupt */
+#define ICR_SADIE	(1 << 13)	   /* slave address detected int enable */
+#define ICR_UR		(1 << 14)	   /* unit reset */
+#define ICR_FM		(1 << 15)	   /* fast mode */
+#define ICR_HS		(1 << 16)	   /* High Speed mode */
+#define ICR_GPIOEN	(1 << 19)	   /* enable GPIO mode for SCL in HS */
+
+#define ISR_RWM		(1 << 0)	   /* read/write mode */
+#define ISR_ACKNAK	(1 << 1)	   /* ack/nak status */
+#define ISR_UB		(1 << 2)	   /* unit busy */
+#define ISR_IBB		(1 << 3)	   /* bus busy */
+#define ISR_SSD		(1 << 4)	   /* slave stop detected */
+#define ISR_ALD		(1 << 5)	   /* arbitration loss detected */
+#define ISR_ITE		(1 << 6)	   /* tx buffer empty */
+#define ISR_IRF		(1 << 7)	   /* rx buffer full */
+#define ISR_GCAD	(1 << 8)	   /* general call address detected */
+#define ISR_SAD		(1 << 9)	   /* slave address detected */
+#define ISR_BED		(1 << 10)	   /* bus error no ACK/NAK */
+
+struct pxa_i2c_msg {
+	u16 addr;
+	u16 flags;
+#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
+#define I2C_M_RD		0x0001	/* read data, from slave to master */
+#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
+#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
+#define I2C_M_FIRST		0x0800	/* First message in a chain */
+	u16 len;
+	u8 *buf;
+};
+
+struct pxa_i2c_state {
+	struct pxa_i2c_msg	*msg;
+	unsigned int		msg_num;
+	unsigned int		msg_ptr;
+};
+
+#define STD_I2C_BASE 0x40301680
+#if defined(CONFIG_CPU_PXA27X) || defined(CONFIG_CPU_PXA25X)
+#define PWR_I2C_BASE	0x40f00180
+#define IBMR_OFFSET	0x00
+#define IDBR_OFFSET	0x08
+#define ICR_OFFSET	0x10
+#define ISR_OFFSET	0x18
+#define ISAR_OFFSET	0x20
+#elif defined(CONFIG_CPU_MONAHANS)
+#define PWR_I2C_BASE	0x40f500c0
+#define IBMR_OFFSET	0x00
+#define IDBR_OFFSET	0x04
+#define ICR_OFFSET	0x08
+#define ISR_OFFSET	0x0c
+#define ISAR_OFFSET	0x10
+#endif
+
+static inline void __iomem *
+pxa_i2c_base(const struct i2c_adapter *adap)
+{
+	return (adap->hwadapnr == 0) ? (void *)STD_I2C_BASE : (void *)PWR_I2C_BASE;
+}
+
+static inline void __iomem *
+pxa_i2c_ibmr(const struct i2c_adapter *adap)
+{
+	return (void *)((char *)pxa_i2c_base(adap) + IBMR_OFFSET);
+}
+
+static inline void __iomem *
+pxa_i2c_idbr(const struct i2c_adapter *adap)
+{
+	return (void *)((char *)pxa_i2c_base(adap) + IDBR_OFFSET);
+}
+
+static inline void __iomem *
+pxa_i2c_icr(const struct i2c_adapter *adap)
+{
+	return (void *)((char *)pxa_i2c_base(adap) + ICR_OFFSET);
+}
+
+static inline void __iomem *
+pxa_i2c_isr(const struct i2c_adapter *adap)
+{
+	return (void *)((char *)pxa_i2c_base(adap) + ISR_OFFSET);
+}
+
+static inline void __iomem *
+pxa_i2c_isar(const struct i2c_adapter *adap)
+{
+	return (void *)((char *)pxa_i2c_base(adap) + ISAR_OFFSET);
+}
+
+#define _IBMR(adap)	pxa_i2c_ibmr(adap)
+#define _IDBR(adap)	pxa_i2c_idbr(adap)
+#define _ICR(adap)	pxa_i2c_icr(adap)
+#define _ISR(adap)	pxa_i2c_isr(adap)
+#define _ISAR(adap)	pxa_i2c_isar(adap)
+
+#ifdef DEBUG
+
+struct bits {
+	u32		mask;
+	const char	*set;
+	const char	*unset;
+};
+#define PXA_BIT(m, s, u)	{ .mask = m, .set = s, .unset = u }
+
+static const struct bits isr_bits[] = {
+	PXA_BIT(ISR_RWM,	"RX",		"TX"),
+	PXA_BIT(ISR_ACKNAK,	"NAK",		"ACK"),
+	PXA_BIT(ISR_UB,		"Bsy",		"Rdy"),
+	PXA_BIT(ISR_IBB,	"BusBsy",	"BusRdy"),
+	PXA_BIT(ISR_SSD,	"SlaveStop",	NULL),
+	PXA_BIT(ISR_ALD,	"ALD",		NULL),
+	PXA_BIT(ISR_ITE,	"TxEmpty",	NULL),
+	PXA_BIT(ISR_IRF,	"RxFull",	NULL),
+	PXA_BIT(ISR_GCAD,	"GenCall",	NULL),
+	PXA_BIT(ISR_SAD,	"SlaveAddr",	NULL),
+	PXA_BIT(ISR_BED,	"BusErr",	NULL),
+};
+
+static const struct bits icr_bits[] = {
+	PXA_BIT(ICR_START,	"START",	NULL),
+	PXA_BIT(ICR_STOP,	"STOP",		NULL),
+	PXA_BIT(ICR_ACKNAK,	"ACKNAK",	NULL),
+	PXA_BIT(ICR_TB,		"TB",		NULL),
+	PXA_BIT(ICR_MA,		"MA",		NULL),
+	PXA_BIT(ICR_SCLE,	"SCLE",		"scle"),
+	PXA_BIT(ICR_IUE,	"IUE",		"iue"),
+	PXA_BIT(ICR_GCD,	"GCD",		NULL),
+	PXA_BIT(ICR_ITEIE,	"ITEIE",	NULL),
+	PXA_BIT(ICR_IRFIE,	"IRFIE",	NULL),
+	PXA_BIT(ICR_BEIE,	"BEIE",		NULL),
+	PXA_BIT(ICR_SSDIE,	"SSDIE",	NULL),
+	PXA_BIT(ICR_ALDIE,	"ALDIE",	NULL),
+	PXA_BIT(ICR_SADIE,	"SADIE",	NULL),
+	PXA_BIT(ICR_UR,		"UR",		"ur"),
+};
+
+static inline void
+decode_bits(const char *prefix, const struct bits *bits, int num, u32 val)
+{
+	printf("%s %08x: ", prefix, val);
+	while (num--) {
+		const char *str = val & bits->mask ? bits->set : bits->unset;
+		if (str)
+			printf("%s ", str);
+		bits++;
+	}
+	printf("\n");
+}
+
+static void
+decode_ISR(unsigned int val)
+{
+	decode_bits("ISR", isr_bits, ARRAY_SIZE(isr_bits), val);
+}
+
+static void
+decode_ICR(unsigned int val)
+{
+	decode_bits("ICR", icr_bits, ARRAY_SIZE(icr_bits), val);
+}
+
+static void i2c_pxa_show_state(struct i2c_adapter *adap, int lno,
+			       const char *fname)
+{
+	u32 icr = readl(_ICR(adap));
+	u32 isr = readl(_ISR(adap));
+	dev_dbg(adap, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno,
+		icr, isr, readl(_IBMR(adap)));
+	printf("    ");
+	decode_ICR(icr);
+	printf("    ");
+	decode_ISR(isr);
+}
+
+#define show_state(adap) i2c_pxa_show_state(adap, __LINE__, __func__)
+
+static void i2c_pxa_scream_blue_murder(struct i2c_adapter *adap,
+				       struct pxa_i2c_state *msgs,
+				       const char *why)
+{
+	dev_dbg(adap, "error: %s\n", why);
+	show_state(adap);
+}
+
+#else /* ifdef DEBUG */
+
+#define show_state(adap) do { } while (0)
+#define decode_ISR(val) do { } while (0)
+#define decode_ICR(val) do { } while (0)
+#define i2c_pxa_scream_blue_murder(adap, msgs, why) do { } while (0)
+
+#endif /* ifdef DEBUG / else */
+
+static enum i2c_code
+i2c_pxa_handler(struct i2c_adapter *adap,
+		struct pxa_i2c_state *msgs);
+
+static void
+i2c_pxa_abort(struct i2c_adapter *adap)
+{
+	unsigned long long end;
+
+	end = get_ticks() + (get_tbclk() / 1000) * ABORT_LENGTH_MS;
+
+	while ((get_ticks() < end) &&
+	       (readl(_IBMR(adap)) & 0x1) == 0) {
+		unsigned long icr = readl(_ICR(adap));
+
+		icr &= ~ICR_START;
+		icr |= ICR_ACKNAK | ICR_STOP | ICR_TB;
+
+		writel(icr, _ICR(adap));
+
+		show_state(adap);
+
+		mdelay(1);
+	}
+
+	writel(readl(_ICR(adap)) & ~(ICR_MA | ICR_START | ICR_STOP),
+	       _ICR(adap));
+}
+
+static void
+i2c_pxa_reset(struct i2c_adapter *adap)
+{
+	/* abort any transfer currently under way */
+	i2c_pxa_abort(adap);
+
+	/* reset according to 9.8 */
+	writel(ICR_UR, _ICR(adap));
+	writel(I2C_ISR_INIT, _ISR(adap));
+	writel(readl(_ICR(adap)) & ~ICR_UR, _ICR(adap));
+
+	writel(adap->slaveaddr, _ISAR(adap));
+
+	/* set control register values */
+	writel(I2C_ICR_INIT, _ICR(adap));
+
+	/* enable unit */
+	writel(readl(_ICR(adap)) | ICR_IUE, _ICR(adap));
+	udelay(100);
+}
+
+static inline unsigned int
+i2c_pxa_addr_byte(struct pxa_i2c_msg *msg)
+{
+	unsigned int addr = (msg->addr & 0x7f) << 1;
+
+	if (msg->flags & I2C_M_RD)
+		addr |= 1;
+
+	return addr;
+}
+
+static inline void
+i2c_pxa_start_message(struct i2c_adapter *adap, struct pxa_i2c_state *msgs)
+{
+	u32 icr;
+	/*
+	 * Step 1: target slave address into IDBR
+	 */
+	writel(i2c_pxa_addr_byte(msgs->msg), _IDBR(adap));
+
+	/*
+	 * Step 2: initiate the write.
+	 */
+	icr = readl(_ICR(adap)) & ~(ICR_STOP | ICR_ALDIE);
+	writel(icr | ICR_START | ICR_TB, _ICR(adap));
+}
+
+static inline void
+i2c_pxa_stop_message(struct i2c_adapter *adap, struct pxa_i2c_state *msgs)
+{
+	u32 icr;
+
+	/*
+	 * Clear the STOP and ACK flags
+	 */
+	icr = readl(_ICR(adap));
+	icr &= ~(ICR_STOP | ICR_ACKNAK);
+	writel(icr, _ICR(adap));
+}
+
+static enum i2c_code
+i2c_pxa_pio_set_master(struct i2c_adapter *adap)
+{
+	/* make timeout the same as for interrupt based functions */
+	unsigned long long end;
+
+	/*
+	 * Wait for the bus to become free.
+	 */
+	end = get_ticks() + (get_tbclk() / 1000) * WAIT_MASTER_MS;
+	while (readl(_ISR(adap)) & (ISR_IBB | ISR_UB)) {
+		udelay(1000);
+		show_state(adap);
+		if (get_ticks() > end) {
+			show_state(adap);
+			dev_err(adap, "i2c_pxa: timeout waiting for bus free\n");
+			return I2C_RETRY;
+		}
+	}
+
+	/*
+	 * Set master mode.
+	 */
+	writel(readl(_ICR(adap)) | ICR_SCLE, _ICR(adap));
+
+	return I2C_SUCCESS;
+}
+
+static enum i2c_code
+i2c_pxa_do_pio_xfer(struct i2c_adapter *adap,
+		    struct pxa_i2c_msg *msg, int num)
+{
+	unsigned long long end;
+	enum i2c_code ret;
+	struct pxa_i2c_state msgs;
+
+	ret = i2c_pxa_pio_set_master(adap);
+	if (ret)
+		goto out;
+
+	memset(&msgs, 0, sizeof(msgs));
+	msgs.msg = msg;
+	msgs.msg_num = num;
+	msg->flags |= I2C_M_FIRST;
+
+	i2c_pxa_start_message(adap, &msgs);
+
+	ret = I2C_TIMEOUT;
+	end = get_ticks() + (get_tbclk() / 1000) * MAX_TRANSFER_MS;
+	while (get_ticks() < end) {
+		enum i2c_code temp_ret;
+		temp_ret = i2c_pxa_handler(adap, &msgs);
+		if (temp_ret != I2C_CONTINUE) {
+			ret = temp_ret;
+			break;
+		}
+		udelay(10);
+	}
+
+	i2c_pxa_stop_message(adap, &msgs);
+
+out:
+	if (ret == I2C_TIMEOUT)
+		i2c_pxa_scream_blue_murder(adap, &msgs, "timeout");
+
+	return ret;
+}
+
+static int
+i2c_pxa_pio_xfer(struct i2c_adapter *adap,
+		 struct pxa_i2c_msg msgs[], int num)
+{
+	int ret, i;
+
+	/* If the I2C controller is disabled we need to reset it
+	  (probably due to a suspend/resume destroying state). We do
+	  this here as we can then avoid worrying about resuming the
+	  controller before its users. */
+	if (!(readl(_ICR(adap)) & ICR_IUE))
+		i2c_pxa_reset(adap);
+
+	for (i = 0; i < 5; i++) {
+		ret = i2c_pxa_do_pio_xfer(adap, msgs, num);
+		if (ret != I2C_RETRY) {
+			goto out;
+		}
+
+		udelay(100);
+	}
+	i2c_pxa_scream_blue_murder(adap, NULL, "exhausted retries");
+	ret = -EREMOTEIO;
+ out:
+	return ret;
+}
+
+
+static enum i2c_code
+i2c_pxa_irq_txempty(struct i2c_adapter *adap,
+		    struct pxa_i2c_state *msgs, u32 isr)
+{
+	enum i2c_code ret = I2C_CONTINUE;
+	u32 icr = readl(_ICR(adap)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB);
+
+ again:
+	/*
+	 * If ISR_ALD is set, we lost arbitration.
+	 */
+	if (isr & ISR_ALD) {
+		/*
+		 * Do we need to do anything here?  The PXA docs
+		 * are vague about what happens.
+		 */
+		i2c_pxa_scream_blue_murder(adap, msgs, "ALD set");
+
+		/*
+		 * We ignore this error.  We seem to see spurious ALDs
+		 * for seemingly no reason.  If we handle them as I think
+		 * they should, we end up causing an I2C error, which
+		 * is painful for some systems.
+		 */
+		return I2C_CONTINUE;
+	}
+
+	if (isr & ISR_BED) {
+		ret = I2C_BUS_ERROR;
+
+		/*
+		 * I2C bus error - either the device NAK'd us, or
+		 * something more serious happened.  If we were NAK'd
+		 * on the initial address phase, we can retry.
+		 */
+		if (isr & ISR_ACKNAK) {
+			if (msgs->msg_ptr == 0 && (msgs->msg->flags & I2C_M_FIRST) != 0)
+				ret = I2C_RETRY;
+			else
+				ret = I2C_NAK;
+		}
+	} else if (isr & ISR_RWM) {
+		/*
+		 * Read mode.  We have just sent the address byte, and
+		 * now we must initiate the transfer.
+		 */
+		if (msgs->msg_ptr == msgs->msg->len - 1 &&
+		    msgs->msg_num == 1)
+			icr |= ICR_STOP | ICR_ACKNAK;
+
+		icr |= ICR_ALDIE | ICR_TB;
+	} else if (msgs->msg_ptr < msgs->msg->len) {
+		/*
+		 * Write mode.  Write the next data byte.
+		 */
+		writel(msgs->msg->buf[msgs->msg_ptr++], _IDBR(adap));
+
+		icr |= ICR_ALDIE | ICR_TB;
+
+		/*
+		 * If this is the last byte of the last message, send
+		 * a STOP.
+		 */
+		if (msgs->msg_ptr == msgs->msg->len &&
+		    msgs->msg_num == 1)
+			icr |= ICR_STOP;
+	} else if (msgs->msg_num > 1) {
+		/*
+		 * Next segment of the message.
+		 */
+		msgs->msg_ptr = 0;
+		msgs->msg_num--;
+		msgs->msg++;
+
+		/*
+		 * If we aren't doing a repeated start and address,
+		 * go back and try to send the next byte.  Note that
+		 * we do not support switching the R/W direction here.
+		 */
+		if (msgs->msg->flags & I2C_M_NOSTART)
+			goto again;
+
+		/*
+		 * Write the next address.
+		 */
+		writel(i2c_pxa_addr_byte(msgs->msg), _IDBR(adap));
+
+		/*
+		 * And trigger a repeated start, and send the byte.
+		 */
+		icr &= ~ICR_ALDIE;
+		icr |= ICR_START | ICR_TB;
+	} else {
+		if ((msgs->msg->flags & I2C_M_FIRST) != 0 &&
+		    msgs->msg->len == 0) {
+			/*
+			 * Device probes have a message length of zero
+			 * and need the bus to be reset before it can
+			 * be used again.
+			 */
+			i2c_pxa_reset(adap);
+		}
+		ret = I2C_SUCCESS;
+	}
+
+	writel(icr, _ICR(adap));
+	show_state(adap);
+	return ret;
+}
+
+static enum i2c_code
+i2c_pxa_irq_rxfull(struct i2c_adapter *adap,
+		   struct pxa_i2c_state *msgs, u32 isr)
+{
+	u32 icr = readl(_ICR(adap)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB);
+	enum i2c_code ret = I2C_CONTINUE;
+
+	/*
+	 * Read the byte.
+	 */
+	msgs->msg->buf[msgs->msg_ptr++] = readl(_IDBR(adap));
+
+	if (msgs->msg_ptr < msgs->msg->len) {
+		/*
+		 * If this is the last byte of the last
+		 * message, send a STOP.
+		 */
+		if (msgs->msg_ptr == msgs->msg->len - 1)
+			icr |= ICR_STOP | ICR_ACKNAK;
+
+		icr |= ICR_ALDIE | ICR_TB;
+	} else {
+		ret = I2C_SUCCESS;
+	}
+
+	writel(icr, _ICR(adap));
+	return ret;
+}
+
+#define VALID_INT_SOURCE	(ISR_SSD | ISR_ALD | ISR_ITE | ISR_IRF | \
+				ISR_SAD | ISR_BED)
+static enum i2c_code
+i2c_pxa_handler(struct i2c_adapter *adap,
+		struct pxa_i2c_state *msgs)
+{
+	u32 isr = readl(_ISR(adap));
+	enum i2c_code ret = I2C_CONTINUE;
+
+	dev_dbg(adap, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n",
+		__func__, isr, readl(_ICR(adap)), readl(_IBMR(adap)));
+	decode_ISR(isr);
+
+	if (!(isr & VALID_INT_SOURCE))
+		return ret;
+
+	show_state(adap);
+
+	/*
+	 * Always clear all pending IRQs.
+	 */
+	writel(isr & VALID_INT_SOURCE, _ISR(adap));
+
+	if (msgs->msg) {
+		if (ret == I2C_CONTINUE && (isr & ISR_ITE))
+			ret = i2c_pxa_irq_txempty(adap, msgs, isr);
+		if (ret == I2C_CONTINUE && (isr & ISR_IRF))
+			ret = i2c_pxa_irq_rxfull(adap, msgs, isr);
+	} else {
+		i2c_pxa_scream_blue_murder(adap, msgs, "spurious irq");
+	}
+	return ret;
+}
+
+static void
+pxa_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
+{
+	i2c_pxa_reset(adap);
+}
+
+static void __maybe_unused
+pxa_i2c_std_init(struct i2c_adapter *adap, int speed, int slaveaddr)
+{
+#if defined(CONFIG_CPU_MONAHANS)
+	writel(readl(CKENB) | CKENB_4_I2C, CKENB);
+#else
+	writel(readl(CKEN) | CKEN14_I2C, CKEN);
+#endif
+	pxa_i2c_init(adap, speed, slaveaddr);
+}
+
+static void __maybe_unused
+pxa_i2c_pwr_init(struct i2c_adapter *adap, int speed, int slaveaddr)
+{
+#if !defined(CONFIG_CPU_MONAHANS)
+	writel(readl(PCFR) | PCFR_PI2C_EN, PCFR);
+	writel(readl(CKEN) | CKEN15_PWRI2C, CKEN);
+#endif
+	pxa_i2c_init(adap, speed, slaveaddr);
+}
+
+static uint8_t *
+addr_to_buffer(uint8_t *buffer, uint addr, int alen)
+{
+	int shift = (alen - 1) * 8;
+	int i = 0;
+	while (alen-- > 0) {
+		buffer[i++] = addr >> shift;
+		shift -= 8;
+	}
+	return buffer;
+}
+
+static int
+pxa_i2c_read(struct i2c_adapter *adap, uint8_t chip, uint addr, int alen,
+	     uint8_t *buffer, int len)
+{
+	uint8_t address_buf[4];
+	struct pxa_i2c_msg msgs[2] = {
+		[0] = {
+			.addr = chip,
+			.flags = 0,
+			.len = alen,
+			.buf = addr_to_buffer(address_buf, addr, alen),
+		},
+		[1] = {
+			.addr = chip,
+			.flags = I2C_M_RD,
+			.len = len,
+			.buf = buffer
+		},
+	};
+	dev_dbg(adap, "reading %d bytes from 0x%02x[0x%x]\n",
+		(int)len, (int)chip, (int)addr);
+
+	return i2c_pxa_pio_xfer(adap, msgs, 2);
+}
+
+
+static int
+pxa_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr,
+	      int alen, uint8_t *buffer, int len)
+{
+	uint8_t address_buf[4];
+	struct pxa_i2c_msg msgs[2] = {
+		[0] = {
+			.addr = chip,
+			.flags = 0,
+			.len = alen,
+			.buf = addr_to_buffer(address_buf, addr, alen),
+		},
+		[1] = {
+			.addr = 0,
+			.flags = I2C_M_NOSTART,
+			.len = len,
+			.buf = (uint8_t *)buffer
+		},
+	};
+	dev_dbg(adap, "writing %d bytes to 0x%02x[0x%x]\n",
+		(int)len, (int)chip, (int)addr);
+
+	return i2c_pxa_pio_xfer(adap, msgs, (len == 0 ? 1 : 2));
+}
+
+static int
+pxa_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
+{
+	struct pxa_i2c_msg msgs[1] = {
+		[0] = {
+			.addr = chip,
+			.flags = 0,
+			.len = 0,
+			.buf = 0,
+		},
+	};
+	if (adap->slaveaddr == chip) {
+		return 1;
+	}
+	dev_dbg(adap, "probing 0x%02x\n", (int)chip);
+
+	return i2c_pxa_pio_xfer(adap, msgs, 1);
+}
+
+#if defined(CONFIG_PXA_STD_I2C)
+U_BOOT_I2C_ADAP_COMPLETE(pxa_std_i2c, pxa_i2c_std_init, pxa_i2c_probe, pxa_i2c_read,
+			 pxa_i2c_write, NULL, CONFIG_SYS_PXA_STD_I2C_SPEED,
+			 CONFIG_SYS_PXA_STD_I2C_SLAVE, 0);
+#endif
+
+#if defined(CONFIG_PXA_PWR_I2C)
+U_BOOT_I2C_ADAP_COMPLETE(pxa_pwr_i2c, pxa_i2c_pwr_init, pxa_i2c_probe, pxa_i2c_read,
+			 pxa_i2c_write, NULL, CONFIG_SYS_PXA_PWR_I2C_SPEED,
+			 CONFIG_SYS_PXA_PWR_I2C_SLAVE, 1);
+#endif
-- 
2.1.4



More information about the U-Boot mailing list