mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 11:46:19 +00:00
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>
This commit is contained in:
parent
ed5e1dd5f2
commit
e28000a38d
1 changed files with 79 additions and 5 deletions
|
@ -49,7 +49,18 @@
|
|||
#define DW_IC_FS_SCL_LCNT 0x20
|
||||
#define DW_IC_INTR_STAT 0x2c
|
||||
#define DW_IC_INTR_MASK 0x30
|
||||
#define DW_IC_RAW_INTR_STAT 0x34
|
||||
#define DW_IC_CLR_INTR 0x40
|
||||
#define DW_IC_CLR_RX_UNDER 0x44
|
||||
#define DW_IC_CLR_RX_OVER 0x48
|
||||
#define DW_IC_CLR_TX_OVER 0x4c
|
||||
#define DW_IC_CLR_RD_REQ 0x50
|
||||
#define DW_IC_CLR_TX_ABRT 0x54
|
||||
#define DW_IC_CLR_RX_DONE 0x58
|
||||
#define DW_IC_CLR_ACTIVITY 0x5c
|
||||
#define DW_IC_CLR_STOP_DET 0x60
|
||||
#define DW_IC_CLR_START_DET 0x64
|
||||
#define DW_IC_CLR_GEN_CALL 0x68
|
||||
#define DW_IC_ENABLE 0x6c
|
||||
#define DW_IC_STATUS 0x70
|
||||
#define DW_IC_TXFLR 0x74
|
||||
|
@ -64,9 +75,18 @@
|
|||
#define DW_IC_CON_RESTART_EN 0x20
|
||||
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
||||
|
||||
#define DW_IC_INTR_TX_EMPTY 0x10
|
||||
#define DW_IC_INTR_TX_ABRT 0x40
|
||||
#define DW_IC_INTR_RX_UNDER 0x001
|
||||
#define DW_IC_INTR_RX_OVER 0x002
|
||||
#define DW_IC_INTR_RX_FULL 0x004
|
||||
#define DW_IC_INTR_TX_OVER 0x008
|
||||
#define DW_IC_INTR_TX_EMPTY 0x010
|
||||
#define DW_IC_INTR_RD_REQ 0x020
|
||||
#define DW_IC_INTR_TX_ABRT 0x040
|
||||
#define DW_IC_INTR_RX_DONE 0x080
|
||||
#define DW_IC_INTR_ACTIVITY 0x100
|
||||
#define DW_IC_INTR_STOP_DET 0x200
|
||||
#define DW_IC_INTR_START_DET 0x400
|
||||
#define DW_IC_INTR_GEN_CALL 0x800
|
||||
|
||||
#define DW_IC_STATUS_ACTIVITY 0x1
|
||||
|
||||
|
@ -439,6 +459,61 @@ static void dw_i2c_pump_msg(unsigned long data)
|
|||
writel(intr_mask, dev->base + DW_IC_INTR_MASK);
|
||||
}
|
||||
|
||||
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 stat;
|
||||
|
||||
/*
|
||||
* The IC_INTR_STAT register just indicates "enabled" interrupts.
|
||||
* Ths unmasked raw version of interrupt status bits are available
|
||||
* in the IC_RAW_INTR_STAT register.
|
||||
*
|
||||
* That is,
|
||||
* stat = readl(IC_INTR_STAT);
|
||||
* equals to,
|
||||
* stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
|
||||
*
|
||||
* The raw version might be useful for debugging purposes.
|
||||
*/
|
||||
stat = readl(dev->base + DW_IC_INTR_STAT);
|
||||
|
||||
/*
|
||||
* Do not use the IC_CLR_INTR register to clear interrupts, or
|
||||
* you'll miss some interrupts, triggered during the period from
|
||||
* readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
|
||||
*
|
||||
* Instead, use the separately-prepared IC_CLR_* registers.
|
||||
*/
|
||||
if (stat & DW_IC_INTR_RX_UNDER)
|
||||
readl(dev->base + DW_IC_CLR_RX_UNDER);
|
||||
if (stat & DW_IC_INTR_RX_OVER)
|
||||
readl(dev->base + DW_IC_CLR_RX_OVER);
|
||||
if (stat & DW_IC_INTR_TX_OVER)
|
||||
readl(dev->base + DW_IC_CLR_TX_OVER);
|
||||
if (stat & DW_IC_INTR_RD_REQ)
|
||||
readl(dev->base + DW_IC_CLR_RD_REQ);
|
||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||
/*
|
||||
* The IC_TX_ABRT_SOURCE register is cleared whenever
|
||||
* the IC_CLR_TX_ABRT is read. Preserve it beforehand.
|
||||
*/
|
||||
dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
|
||||
readl(dev->base + DW_IC_CLR_TX_ABRT);
|
||||
}
|
||||
if (stat & DW_IC_INTR_RX_DONE)
|
||||
readl(dev->base + DW_IC_CLR_RX_DONE);
|
||||
if (stat & DW_IC_INTR_ACTIVITY)
|
||||
readl(dev->base + DW_IC_CLR_ACTIVITY);
|
||||
if (stat & DW_IC_INTR_STOP_DET)
|
||||
readl(dev->base + DW_IC_CLR_STOP_DET);
|
||||
if (stat & DW_IC_INTR_START_DET)
|
||||
readl(dev->base + DW_IC_CLR_START_DET);
|
||||
if (stat & DW_IC_INTR_GEN_CALL)
|
||||
readl(dev->base + DW_IC_CLR_GEN_CALL);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt service routine. This gets called whenever an I2C interrupt
|
||||
* occurs.
|
||||
|
@ -448,16 +523,15 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
|||
struct dw_i2c_dev *dev = dev_id;
|
||||
u32 stat;
|
||||
|
||||
stat = readl(dev->base + DW_IC_INTR_STAT);
|
||||
stat = i2c_dw_read_clear_intrbits(dev);
|
||||
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
|
||||
|
||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
||||
dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
|
||||
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
|
||||
dev->status = STATUS_IDLE;
|
||||
} else if (stat & DW_IC_INTR_TX_EMPTY)
|
||||
tasklet_schedule(&dev->pump_msg);
|
||||
|
||||
readl(dev->base + DW_IC_CLR_INTR); /* clear interrupts */
|
||||
writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
|
||||
if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
|
||||
complete(&dev->cmd_complete);
|
||||
|
|
Loading…
Reference in a new issue