Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e28000a3 authored by Shinya Kuribayashi's avatar Shinya Kuribayashi Committed by Ben Dooks
Browse files

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: default avatarShinya Kuribayashi <shinya.kuribayashi@necel.com>
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
parent ed5e1dd5
Loading
Loading
Loading
Loading
+79 −5
Original line number Diff line number Diff line
@@ -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);