aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses
diff options
context:
space:
mode:
authorTodd Poynor <toddpoynor@google.com>2011-04-25 17:32:25 -0400
committerBen Dooks <ben-linux@fluff.org>2011-05-11 01:45:50 -0400
commitcb63c62d8ee56d169463d8125ec32e1aa0fe11de (patch)
treee52fcd155f0ee846ff132d3fcecf44dde640da1c /drivers/i2c/busses
parent65a1a0ace554d61ea5a90377a54df1505275c1b1 (diff)
i2c: tegra: recover from spurious interrupt storm
Re-init the I2C controller when an IRQ arrives with no I2C_INT_STATUS bits set to indicate why the interrupt was sent. Storms of such mystery interrupts are infrequently seen. Dump some more status when these interrupts arrive. Set an error for the current request and wake up the requester (rather than timing out the request or possibly silently ignoring the interrupts). If the I2C block is inside the DVC, also ACK the DVC I2C transfer done interrupt in the ISR error return path, as is done for the normal return path. Signed-off-by: Todd Poynor <toddpoynor@google.com> [swarren: Fix minor checkpatch whitespace issue, commit tag] Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r--drivers/i2c/busses/i2c-tegra.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 5f1b92c74bd..c0b9aa7df0e 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -37,6 +37,7 @@
37#define I2C_CNFG 0x000 37#define I2C_CNFG 0x000
38#define I2C_CNFG_PACKET_MODE_EN (1<<10) 38#define I2C_CNFG_PACKET_MODE_EN (1<<10)
39#define I2C_CNFG_NEW_MASTER_FSM (1<<11) 39#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
40#define I2C_STATUS 0x01C
40#define I2C_SL_CNFG 0x020 41#define I2C_SL_CNFG 0x020
41#define I2C_SL_CNFG_NEWSL (1<<2) 42#define I2C_SL_CNFG_NEWSL (1<<2)
42#define I2C_SL_ADDR1 0x02c 43#define I2C_SL_ADDR1 0x02c
@@ -77,6 +78,7 @@
77#define I2C_ERR_NONE 0x00 78#define I2C_ERR_NONE 0x00
78#define I2C_ERR_NO_ACK 0x01 79#define I2C_ERR_NO_ACK 0x01
79#define I2C_ERR_ARBITRATION_LOST 0x02 80#define I2C_ERR_ARBITRATION_LOST 0x02
81#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
80 82
81#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28 83#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
82#define PACKET_HEADER0_PACKET_ID_SHIFT 16 84#define PACKET_HEADER0_PACKET_ID_SHIFT 16
@@ -121,6 +123,7 @@ struct tegra_i2c_dev {
121 void __iomem *base; 123 void __iomem *base;
122 int cont_id; 124 int cont_id;
123 int irq; 125 int irq;
126 bool irq_disabled;
124 int is_dvc; 127 int is_dvc;
125 struct completion msg_complete; 128 struct completion msg_complete;
126 int msg_err; 129 int msg_err;
@@ -343,6 +346,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
343 err = -ETIMEDOUT; 346 err = -ETIMEDOUT;
344 347
345 clk_disable(i2c_dev->clk); 348 clk_disable(i2c_dev->clk);
349
350 if (i2c_dev->irq_disabled) {
351 i2c_dev->irq_disabled = 0;
352 enable_irq(i2c_dev->irq);
353 }
354
346 return err; 355 return err;
347} 356}
348 357
@@ -355,8 +364,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
355 status = i2c_readl(i2c_dev, I2C_INT_STATUS); 364 status = i2c_readl(i2c_dev, I2C_INT_STATUS);
356 365
357 if (status == 0) { 366 if (status == 0) {
358 dev_warn(i2c_dev->dev, "interrupt with no status\n"); 367 dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
359 return IRQ_NONE; 368 i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
369 i2c_readl(i2c_dev, I2C_STATUS),
370 i2c_readl(i2c_dev, I2C_CNFG));
371 i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
372
373 if (!i2c_dev->irq_disabled) {
374 disable_irq_nosync(i2c_dev->irq);
375 i2c_dev->irq_disabled = 1;
376 }
377
378 complete(&i2c_dev->msg_complete);
379 goto err;
360 } 380 }
361 381
362 if (unlikely(status & status_err)) { 382 if (unlikely(status & status_err)) {
@@ -396,6 +416,8 @@ err:
396 I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ | 416 I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
397 I2C_INT_RX_FIFO_DATA_REQ); 417 I2C_INT_RX_FIFO_DATA_REQ);
398 i2c_writel(i2c_dev, status, I2C_INT_STATUS); 418 i2c_writel(i2c_dev, status, I2C_INT_STATUS);
419 if (i2c_dev->is_dvc)
420 dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
399 return IRQ_HANDLED; 421 return IRQ_HANDLED;
400} 422}
401 423