From: Nishanth Menon Date: Sat, 12 May 2007 00:36:02 +0000 (-0500) Subject: FIFO Handling support and broken hw workaround X-Git-Tag: v2.6.22-omap1~144 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=c77e8cffdbf466d4baaa05e78ce71afe56652257;p=linux-2.6-omap-h63xx.git FIFO Handling support and broken hw workaround *) xfers based can use FIFO on FIFO capable devices *) Prevents errors for HSI2C if FIFO is not used *) Should co-exist with no-FIFO support h/w also *) Implemented errenous handling of STT-STP handling on SDP2430 Signed-off-by: Nishanth Menon Signed-off-by: Tony Lindgren --- diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index fcc65929833..1441ac87dbc 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -61,8 +61,11 @@ #define OMAP_I2C_SCLL_REG 0x34 #define OMAP_I2C_SCLH_REG 0x38 #define OMAP_I2C_SYSTEST_REG 0x3c +#define OMAP_I2C_BUFSTAT_REG 0x40 /* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer draining int enable */ +#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer draining int enable */ #define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ #define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ #define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ @@ -70,7 +73,8 @@ #define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ /* I2C Status Register (OMAP_I2C_STAT): */ -#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define OMAP_I2C_STAT_XDR (1 << 14) /* TX Buffer draining */ +#define OMAP_I2C_STAT_RDR (1 << 13) /* RX Buffer draining */ #define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ #define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ #define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ @@ -84,12 +88,14 @@ /* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ #define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */ #define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ +#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */ /* I2C Configuration Register (OMAP_I2C_CON): */ #define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ #define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ -#define OMAP_I2C_CON_OPMODE (1 << 12) /* High Speed support */ +#define OMAP_I2C_CON_OPMODE_HS (1 << 12) /* High Speed support */ #define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ #define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ #define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ @@ -133,7 +139,12 @@ struct omap_i2c_dev { u8 *buf; size_t buf_len; struct i2c_adapter adapter; + u8 fifo_size; /* use as flag and value + * fifo_size==0 implies no fifo + * if set, should be trsh+1 + */ unsigned rev1:1; + unsigned b_hw:1; /* bad h/w fixes */ }; static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, @@ -296,6 +307,14 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll); omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh); + if (dev->fifo_size) + /* Note: setup required fifo size - 1 */ + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, + (dev->fifo_size - 1) << 8 | /* RTRSH */ + OMAP_I2C_BUF_RXFIF_CLR | + (dev->fifo_size - 1) | /* XTRSH */ + OMAP_I2C_BUF_TXFIF_CLR); + /* Take the I2C module out of reset: */ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); @@ -303,7 +322,8 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | - OMAP_I2C_IE_AL)); + OMAP_I2C_IE_AL) | ((dev->fifo_size) ? + (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0)); return 0; } @@ -375,6 +395,11 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); + /* Clear the FIFO Buffers */ + w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG); + w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; + omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); + init_completion(&dev->cmd_complete); dev->cmd_err = 0; @@ -382,16 +407,27 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, /* High speed configuration */ if (dev->speed > 400) - w |= OMAP_I2C_CON_OPMODE; + w |= OMAP_I2C_CON_OPMODE_HS; if (msg->flags & I2C_M_TEN) w |= OMAP_I2C_CON_XA; if (!(msg->flags & I2C_M_RD)) w |= OMAP_I2C_CON_TRX; - if (stop) + + if (!dev->b_hw && stop) w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + if (dev->b_hw && stop) { + /* H/w behavior: dont write stt and stp together.. */ + while (omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) & OMAP_I2C_CON_STT) { + /* Dont do anything - this will come in a couple of loops at max*/ + } + w |= OMAP_I2C_CON_STP; + w &= ~OMAP_I2C_CON_STT; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } r = wait_for_completion_timeout(&dev->cmd_complete, OMAP_I2C_TIMEOUT); dev->buf_len = 0; @@ -555,45 +591,73 @@ omap_i2c_isr(int this_irq, void *dev_id) omap_i2c_complete_cmd(dev, 0); continue; } - if (stat & OMAP_I2C_STAT_RRDY) { - w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); - if (dev->buf_len) { - *dev->buf++ = w; - dev->buf_len--; - /* - * Data reg in 2430 is 8 bit wide, - */ - if (!cpu_is_omap2430()) { - if (dev->buf_len) { - *dev->buf++ = w >> 8; - dev->buf_len--; + if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) { + u8 num_bytes = 1; + if (dev->fifo_size) { + num_bytes = (stat & OMAP_I2C_STAT_RRDY) ? dev->fifo_size : + omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG); + } + while (num_bytes) { + num_bytes--; + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + if (dev->buf_len) { + *dev->buf++ = w; + dev->buf_len--; + /* + * Data reg in 2430 is 8 bit wide, + */ + if (!cpu_is_omap2430()) { + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } } + } else { + if (stat & OMAP_I2C_STAT_RRDY) + dev_err(dev->dev, "RRDY IRQ while no data" + "requested\n"); + if (stat & OMAP_I2C_STAT_RDR) + dev_err(dev->dev, "RDR IRQ while no data" + "requested\n"); + break; } - } else - dev_err(dev->dev, "RRDY IRQ while no data" - "requested\n"); - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY); + } + omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)); continue; } - if (stat & OMAP_I2C_STAT_XRDY) { - w = 0; - if (dev->buf_len) { - w = *dev->buf++; - dev->buf_len--; - /* - * Data reg in 2430 is 8 bit wide, - */ - if (!cpu_is_omap2430()) { - if (dev->buf_len) { - w |= *dev->buf++ << 8; - dev->buf_len--; + if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) { + u8 num_bytes = 1; + if (dev->fifo_size) { + num_bytes = (stat & OMAP_I2C_STAT_XRDY) ? dev->fifo_size : + omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG); + } + while (num_bytes) { + num_bytes--; + w = 0; + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + /* + * Data reg in 2430 is 8 bit wide, + */ + if (!cpu_is_omap2430()) { + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } } + } else { + if (stat & OMAP_I2C_STAT_XRDY) + dev_err(dev->dev, "XRDY IRQ while no" + "data to send\n"); + if (stat & OMAP_I2C_STAT_XDR) + dev_err(dev->dev, "XDR IRQ while no" + "data to send\n"); + break; } - } else - dev_err(dev->dev, "XRDY IRQ while no" - "data to send\n"); - omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY); + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } + omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); continue; } if (stat & OMAP_I2C_STAT_ROVR) { @@ -676,6 +740,19 @@ omap_i2c_probe(struct platform_device *pdev) if (cpu_is_omap15xx()) dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; + if (cpu_is_omap2430()) { + /* Set up the fifo size - Get total size */ + dev->fifo_size = 0x8 << + ((omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3); + /* + * Set up notification threshold as half the total available size + * This is to ensure that we can handle the status on int call back + * latencies + */ + dev->fifo_size = (dev->fifo_size / 2); + dev->b_hw = 1; /* Enable hardware fixes */ + } + /* reset ASAP, clearing any IRQs */ omap_i2c_init(dev);