
Cc: Marek Vasut marex@denx.de Cc: Heiko Schocher hs@denx.de Signed-off-by: Andrew Ruder andrew.ruder@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