[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