Cc: Marek Vasut <[email protected]>
Cc: Heiko Schocher <[email protected]>
Signed-off-by: Andrew Ruder <[email protected]>
---

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

_______________________________________________
U-Boot mailing list
[email protected]
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to