aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorShinya Kuribayashi <shinya.kuribayashi@necel.com>2009-11-06 07:47:51 -0500
committerBen Dooks <ben-linux@fluff.org>2009-12-08 19:19:11 -0500
commit0774539948b23984f1c866135ba307fa2c441d0e (patch)
tree5e6ef7e974326a25814134148ceef34be8d0c947 /drivers/i2c
parentc70c5cd37413c3fa3503212d26ffdf6df535c9de (diff)
i2c-designware: Process i2c_msg messages in the interrupt handler
Symptom: -------- When we're going to send/receive the longer size of data than the Tx FIFO length, the I2C transaction will be divided into several separated transactions, limited by the Tx FIFO length. Details: -------- As a hardware feature, DW I2C core generates a STOP condition whenever the Tx FIFO becomes empty (strictly speaking, whenever the last byte in the Tx FIFO is sent out), even if we have more bytes to be written. Then, once a new transmit data is written to the Tx FIFO, DW I2C core will initiate a new transaction, which leads to another START condition. This explains how the transaction in question goes, and implies that current tasklet-based dw_i2c_pump_msg() strategy couldn't meet the timing constraint required for avoiding Tx FIFO underrun. To avoid this scenario, we must keep providing new transmit data within a given time period. In case of Fast-mode + 32-byte Tx FIFO, for instance, it takes about 22.5[us] to process single byte, and 720[us] in total. This patch removes the existing tasklet-based "pump" system, and move its jobs into the interrupt handler. Signed-off-by: Shinya Kuribayashi <shinya.kuribayashi@necel.com> Acked-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-designware.c39
1 files changed, 14 insertions, 25 deletions
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index bfb42f0f87a6..5fce1a07e6c1 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -149,7 +149,6 @@ static char *abort_sources[] = {
149 * @dev: driver model device node 149 * @dev: driver model device node
150 * @base: IO registers pointer 150 * @base: IO registers pointer
151 * @cmd_complete: tx completion indicator 151 * @cmd_complete: tx completion indicator
152 * @pump_msg: continue in progress transfers
153 * @lock: protect this struct and IO registers 152 * @lock: protect this struct and IO registers
154 * @clk: input reference clock 153 * @clk: input reference clock
155 * @cmd_err: run time hadware error code 154 * @cmd_err: run time hadware error code
@@ -175,7 +174,6 @@ struct dw_i2c_dev {
175 struct device *dev; 174 struct device *dev;
176 void __iomem *base; 175 void __iomem *base;
177 struct completion cmd_complete; 176 struct completion cmd_complete;
178 struct tasklet_struct pump_msg;
179 struct mutex lock; 177 struct mutex lock;
180 struct clk *clk; 178 struct clk *clk;
181 int cmd_err; 179 int cmd_err;
@@ -325,7 +323,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
325/* 323/*
326 * Initiate low level master read/write transaction. 324 * Initiate low level master read/write transaction.
327 * This function is called from i2c_dw_xfer when starting a transfer. 325 * This function is called from i2c_dw_xfer when starting a transfer.
328 * This function is also called from dw_i2c_pump_msg to continue a transfer 326 * This function is also called from i2c_dw_isr to continue a transfer
329 * that is longer than the size of the TX FIFO. 327 * that is longer than the size of the TX FIFO.
330 */ 328 */
331static void 329static void
@@ -489,10 +487,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
489 487
490 /* no error */ 488 /* no error */
491 if (likely(!dev->cmd_err)) { 489 if (likely(!dev->cmd_err)) {
492 /* read rx fifo, and disable the adapter */ 490 /* Disable the adapter */
493 do {
494 i2c_dw_read(dev);
495 } while (dev->status & STATUS_READ_IN_PROGRESS);
496 writel(0, dev->base + DW_IC_ENABLE); 491 writel(0, dev->base + DW_IC_ENABLE);
497 ret = num; 492 ret = num;
498 goto done; 493 goto done;
@@ -520,20 +515,6 @@ static u32 i2c_dw_func(struct i2c_adapter *adap)
520 return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; 515 return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
521} 516}
522 517
523static void dw_i2c_pump_msg(unsigned long data)
524{
525 struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
526 u32 intr_mask;
527
528 i2c_dw_read(dev);
529 i2c_dw_xfer_msg(dev);
530
531 intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
532 if (dev->status & STATUS_WRITE_IN_PROGRESS)
533 intr_mask |= DW_IC_INTR_TX_EMPTY;
534 writel(intr_mask, dev->base + DW_IC_INTR_MASK);
535}
536
537static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) 518static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
538{ 519{
539 u32 stat; 520 u32 stat;
@@ -604,10 +585,19 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
604 if (stat & DW_IC_INTR_TX_ABRT) { 585 if (stat & DW_IC_INTR_TX_ABRT) {
605 dev->cmd_err |= DW_IC_ERR_TX_ABRT; 586 dev->cmd_err |= DW_IC_ERR_TX_ABRT;
606 dev->status = STATUS_IDLE; 587 dev->status = STATUS_IDLE;
607 } else if (stat & DW_IC_INTR_TX_EMPTY) 588 }
608 tasklet_schedule(&dev->pump_msg); 589
590 if (stat & DW_IC_INTR_TX_EMPTY) {
591 i2c_dw_read(dev);
592 i2c_dw_xfer_msg(dev);
593 }
594
595 /*
596 * No need to modify or disable the interrupt mask here.
597 * i2c_dw_xfer_msg() will take care of it according to
598 * the current transmit status.
599 */
609 600
610 writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
611 if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) 601 if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
612 complete(&dev->cmd_complete); 602 complete(&dev->cmd_complete);
613 603
@@ -653,7 +643,6 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
653 } 643 }
654 644
655 init_completion(&dev->cmd_complete); 645 init_completion(&dev->cmd_complete);
656 tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
657 mutex_init(&dev->lock); 646 mutex_init(&dev->lock);
658 dev->dev = get_device(&pdev->dev); 647 dev->dev = get_device(&pdev->dev);
659 dev->irq = irq; 648 dev->irq = irq;