mirror of
https://github.com/adulau/aha.git
synced 2024-12-30 20:56:23 +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_FS_SCL_LCNT 0x20
|
||||||
#define DW_IC_INTR_STAT 0x2c
|
#define DW_IC_INTR_STAT 0x2c
|
||||||
#define DW_IC_INTR_MASK 0x30
|
#define DW_IC_INTR_MASK 0x30
|
||||||
|
#define DW_IC_RAW_INTR_STAT 0x34
|
||||||
#define DW_IC_CLR_INTR 0x40
|
#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_ENABLE 0x6c
|
||||||
#define DW_IC_STATUS 0x70
|
#define DW_IC_STATUS 0x70
|
||||||
#define DW_IC_TXFLR 0x74
|
#define DW_IC_TXFLR 0x74
|
||||||
|
@ -64,9 +75,18 @@
|
||||||
#define DW_IC_CON_RESTART_EN 0x20
|
#define DW_IC_CON_RESTART_EN 0x20
|
||||||
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
||||||
|
|
||||||
#define DW_IC_INTR_TX_EMPTY 0x10
|
#define DW_IC_INTR_RX_UNDER 0x001
|
||||||
#define DW_IC_INTR_TX_ABRT 0x40
|
#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_STOP_DET 0x200
|
||||||
|
#define DW_IC_INTR_START_DET 0x400
|
||||||
|
#define DW_IC_INTR_GEN_CALL 0x800
|
||||||
|
|
||||||
#define DW_IC_STATUS_ACTIVITY 0x1
|
#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);
|
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
|
* Interrupt service routine. This gets called whenever an I2C interrupt
|
||||||
* occurs.
|
* occurs.
|
||||||
|
@ -448,16 +523,15 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
|
||||||
struct dw_i2c_dev *dev = dev_id;
|
struct dw_i2c_dev *dev = dev_id;
|
||||||
u32 stat;
|
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);
|
dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
|
||||||
|
|
||||||
if (stat & DW_IC_INTR_TX_ABRT) {
|
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->cmd_err |= DW_IC_ERR_TX_ABRT;
|
||||||
dev->status = STATUS_IDLE;
|
dev->status = STATUS_IDLE;
|
||||||
} else if (stat & DW_IC_INTR_TX_EMPTY)
|
} else if (stat & DW_IC_INTR_TX_EMPTY)
|
||||||
tasklet_schedule(&dev->pump_msg);
|
tasklet_schedule(&dev->pump_msg);
|
||||||
|
|
||||||
readl(dev->base + DW_IC_CLR_INTR); /* clear interrupts */
|
|
||||||
writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
|
writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
|
||||||
if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
|
if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
|
||||||
complete(&dev->cmd_complete);
|
complete(&dev->cmd_complete);
|
||||||
|
|
Loading…
Reference in a new issue