diff options
author | Shinya Kuribayashi <shinya.kuribayashi@necel.com> | 2009-11-06 07:44:37 -0500 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-12-08 19:19:09 -0500 |
commit | e28000a38da803de8d90727bec45f3d7c831a59a (patch) | |
tree | 1d6c6e6294c91cffb32ec05ff3295e2472338938 | |
parent | ed5e1dd5f2daa8a59bc8116888417a6ff96d2ae9 (diff) |
i2c-designware: Don't use the IC_CLR_INTR register to clear interrupts
We're strongly discouraged from using the IC_CLR_INTR register because
it clears all software-clearable interrupts asserted at the moment.
stat = readl(IC_INTR_STAT);
:
: <=== Interrupts asserted during this period will be lost
:
readl(IC_CLR_INTR);
Instead, use the separately-prepared IC_CLR_* registers.
At the same time, this patch adds all remaining interrupt definitions
available in the DesignWare I2C hardware.
Signed-off-by: Shinya Kuribayashi <shinya.kuribayashi@necel.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r-- | drivers/i2c/busses/i2c-designware.c | 84 |
1 files changed, 79 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c index a4f928e1fc5b..eeb1915c59e3 100644 --- a/drivers/i2c/busses/i2c-designware.c +++ b/drivers/i2c/busses/i2c-designware.c | |||
@@ -49,7 +49,18 @@ | |||
49 | #define DW_IC_FS_SCL_LCNT 0x20 | 49 | #define DW_IC_FS_SCL_LCNT 0x20 |
50 | #define DW_IC_INTR_STAT 0x2c | 50 | #define DW_IC_INTR_STAT 0x2c |
51 | #define DW_IC_INTR_MASK 0x30 | 51 | #define DW_IC_INTR_MASK 0x30 |
52 | #define DW_IC_RAW_INTR_STAT 0x34 | ||
52 | #define DW_IC_CLR_INTR 0x40 | 53 | #define DW_IC_CLR_INTR 0x40 |
54 | #define DW_IC_CLR_RX_UNDER 0x44 | ||
55 | #define DW_IC_CLR_RX_OVER 0x48 | ||
56 | #define DW_IC_CLR_TX_OVER 0x4c | ||
57 | #define DW_IC_CLR_RD_REQ 0x50 | ||
58 | #define DW_IC_CLR_TX_ABRT 0x54 | ||
59 | #define DW_IC_CLR_RX_DONE 0x58 | ||
60 | #define DW_IC_CLR_ACTIVITY 0x5c | ||
61 | #define DW_IC_CLR_STOP_DET 0x60 | ||
62 | #define DW_IC_CLR_START_DET 0x64 | ||
63 | #define DW_IC_CLR_GEN_CALL 0x68 | ||
53 | #define DW_IC_ENABLE 0x6c | 64 | #define DW_IC_ENABLE 0x6c |
54 | #define DW_IC_STATUS 0x70 | 65 | #define DW_IC_STATUS 0x70 |
55 | #define DW_IC_TXFLR 0x74 | 66 | #define DW_IC_TXFLR 0x74 |
@@ -64,9 +75,18 @@ | |||
64 | #define DW_IC_CON_RESTART_EN 0x20 | 75 | #define DW_IC_CON_RESTART_EN 0x20 |
65 | #define DW_IC_CON_SLAVE_DISABLE 0x40 | 76 | #define DW_IC_CON_SLAVE_DISABLE 0x40 |
66 | 77 | ||
67 | #define DW_IC_INTR_TX_EMPTY 0x10 | 78 | #define DW_IC_INTR_RX_UNDER 0x001 |
68 | #define DW_IC_INTR_TX_ABRT 0x40 | 79 | #define DW_IC_INTR_RX_OVER 0x002 |
80 | #define DW_IC_INTR_RX_FULL 0x004 | ||
81 | #define DW_IC_INTR_TX_OVER 0x008 | ||
82 | #define DW_IC_INTR_TX_EMPTY 0x010 | ||
83 | #define DW_IC_INTR_RD_REQ 0x020 | ||
84 | #define DW_IC_INTR_TX_ABRT 0x040 | ||
85 | #define DW_IC_INTR_RX_DONE 0x080 | ||
86 | #define DW_IC_INTR_ACTIVITY 0x100 | ||
69 | #define DW_IC_INTR_STOP_DET 0x200 | 87 | #define DW_IC_INTR_STOP_DET 0x200 |
88 | #define DW_IC_INTR_START_DET 0x400 | ||
89 | #define DW_IC_INTR_GEN_CALL 0x800 | ||
70 | 90 | ||
71 | #define DW_IC_STATUS_ACTIVITY 0x1 | 91 | #define DW_IC_STATUS_ACTIVITY 0x1 |
72 | 92 | ||
@@ -439,6 +459,61 @@ static void dw_i2c_pump_msg(unsigned long data) | |||
439 | writel(intr_mask, dev->base + DW_IC_INTR_MASK); | 459 | writel(intr_mask, dev->base + DW_IC_INTR_MASK); |
440 | } | 460 | } |
441 | 461 | ||
462 | static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev) | ||
463 | { | ||
464 | u32 stat; | ||
465 | |||
466 | /* | ||
467 | * The IC_INTR_STAT register just indicates "enabled" interrupts. | ||
468 | * Ths unmasked raw version of interrupt status bits are available | ||
469 | * in the IC_RAW_INTR_STAT register. | ||
470 | * | ||
471 | * That is, | ||
472 | * stat = readl(IC_INTR_STAT); | ||
473 | * equals to, | ||
474 | * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); | ||
475 | * | ||
476 | * The raw version might be useful for debugging purposes. | ||
477 | */ | ||
478 | stat = readl(dev->base + DW_IC_INTR_STAT); | ||
479 | |||
480 | /* | ||
481 | * Do not use the IC_CLR_INTR register to clear interrupts, or | ||
482 | * you'll miss some interrupts, triggered during the period from | ||
483 | * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). | ||
484 | * | ||
485 | * Instead, use the separately-prepared IC_CLR_* registers. | ||
486 | */ | ||
487 | if (stat & DW_IC_INTR_RX_UNDER) | ||
488 | readl(dev->base + DW_IC_CLR_RX_UNDER); | ||
489 | if (stat & DW_IC_INTR_RX_OVER) | ||
490 | readl(dev->base + DW_IC_CLR_RX_OVER); | ||
491 | if (stat & DW_IC_INTR_TX_OVER) | ||
492 | readl(dev->base + DW_IC_CLR_TX_OVER); | ||
493 | if (stat & DW_IC_INTR_RD_REQ) | ||
494 | readl(dev->base + DW_IC_CLR_RD_REQ); | ||
495 | if (stat & DW_IC_INTR_TX_ABRT) { | ||
496 | /* | ||
497 | * The IC_TX_ABRT_SOURCE register is cleared whenever | ||
498 | * the IC_CLR_TX_ABRT is read. Preserve it beforehand. | ||
499 | */ | ||
500 | dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); | ||
501 | readl(dev->base + DW_IC_CLR_TX_ABRT); | ||
502 | } | ||
503 | if (stat & DW_IC_INTR_RX_DONE) | ||
504 | readl(dev->base + DW_IC_CLR_RX_DONE); | ||
505 | if (stat & DW_IC_INTR_ACTIVITY) | ||
506 | readl(dev->base + DW_IC_CLR_ACTIVITY); | ||
507 | if (stat & DW_IC_INTR_STOP_DET) | ||
508 | readl(dev->base + DW_IC_CLR_STOP_DET); | ||
509 | if (stat & DW_IC_INTR_START_DET) | ||
510 | readl(dev->base + DW_IC_CLR_START_DET); | ||
511 | if (stat & DW_IC_INTR_GEN_CALL) | ||
512 | readl(dev->base + DW_IC_CLR_GEN_CALL); | ||
513 | |||
514 | return stat; | ||
515 | } | ||
516 | |||
442 | /* | 517 | /* |
443 | * Interrupt service routine. This gets called whenever an I2C interrupt | 518 | * Interrupt service routine. This gets called whenever an I2C interrupt |
444 | * occurs. | 519 | * occurs. |
@@ -448,16 +523,15 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) | |||
448 | struct dw_i2c_dev *dev = dev_id; | 523 | struct dw_i2c_dev *dev = dev_id; |
449 | u32 stat; | 524 | u32 stat; |
450 | 525 | ||
451 | stat = readl(dev->base + DW_IC_INTR_STAT); | 526 | stat = i2c_dw_read_clear_intrbits(dev); |
452 | dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); | 527 | dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); |
528 | |||
453 | if (stat & DW_IC_INTR_TX_ABRT) { | 529 | if (stat & DW_IC_INTR_TX_ABRT) { |
454 | dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE); | ||
455 | dev->cmd_err |= DW_IC_ERR_TX_ABRT; | 530 | dev->cmd_err |= DW_IC_ERR_TX_ABRT; |
456 | dev->status = STATUS_IDLE; | 531 | dev->status = STATUS_IDLE; |
457 | } else if (stat & DW_IC_INTR_TX_EMPTY) | 532 | } else if (stat & DW_IC_INTR_TX_EMPTY) |
458 | tasklet_schedule(&dev->pump_msg); | 533 | tasklet_schedule(&dev->pump_msg); |
459 | 534 | ||
460 | readl(dev->base + DW_IC_CLR_INTR); /* clear interrupts */ | ||
461 | writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */ | 535 | writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */ |
462 | if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) | 536 | if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) |
463 | complete(&dev->cmd_complete); | 537 | complete(&dev->cmd_complete); |