Skip to content

Commit

Permalink
i2c: rcar: protect against supurious interrupts on V3U
Browse files Browse the repository at this point in the history
V3U creates spurious interrupts which we need to handle. This costs time
until BUS_PHASE_DATA can be activated which is problematic for Gen2 SoCs
and earlier. Because of this we introduce two interrupt handlers here
which will call a generic main irq function once the timing critical
stuff is done.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
  • Loading branch information
Wolfram Sang authored and Wolfram Sang committed Jan 5, 2021
1 parent 24c6d4b commit 9c975c4
Showing 1 changed file with 43 additions and 14 deletions.
57 changes: 43 additions & 14 deletions drivers/i2c/busses/i2c-rcar.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,20 +625,11 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
* generated. It turned out that taking a spinlock at the beginning of the ISR
* was already causing repeated messages. Thus, this driver was converted to
* the now lockless behaviour. Please keep this in mind when hacking the driver.
* R-Car Gen3 seems to have this fixed but earlier versions than R-Car Gen2 are
* likely affected. Therefore, we have different interrupt handler entries.
*/
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
{
struct rcar_i2c_priv *priv = ptr;
u32 msr;

/* Clear START or STOP immediately, except for REPSTART after read */
if (likely(!(priv->flags & ID_P_REP_AFTER_RD)))
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);

msr = rcar_i2c_read(priv, ICMSR);

/* Only handle interrupts that are currently enabled */
msr &= rcar_i2c_read(priv, ICMIER);
if (!msr) {
if (rcar_i2c_slave_irq(priv))
return IRQ_HANDLED;
Expand Down Expand Up @@ -682,6 +673,41 @@ static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
return IRQ_HANDLED;
}

static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
u32 msr;

/* Clear START or STOP immediately, except for REPSTART after read */
if (likely(!(priv->flags & ID_P_REP_AFTER_RD)))
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);

/* Only handle interrupts that are currently enabled */
msr = rcar_i2c_read(priv, ICMSR);
msr &= rcar_i2c_read(priv, ICMIER);

return rcar_i2c_irq(irq, priv, msr);
}

static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
{
struct rcar_i2c_priv *priv = ptr;
u32 msr;

/* Only handle interrupts that are currently enabled */
msr = rcar_i2c_read(priv, ICMSR);
msr &= rcar_i2c_read(priv, ICMIER);

/*
* Clear START or STOP immediately, except for REPSTART after read or
* if a spurious interrupt was detected.
*/
if (likely(!(priv->flags & ID_P_REP_AFTER_RD) && msr))
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);

return rcar_i2c_irq(irq, priv, msr);
}

static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
enum dma_transfer_direction dir,
dma_addr_t port_addr)
Expand Down Expand Up @@ -929,6 +955,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
struct i2c_adapter *adap;
struct device *dev = &pdev->dev;
unsigned long irqflags = 0;
irqreturn_t (*irqhandler)(int irq, void *ptr) = rcar_i2c_gen3_irq;
int ret;

/* Otherwise logic will break because some bytes must always use PIO */
Expand Down Expand Up @@ -977,8 +1004,10 @@ static int rcar_i2c_probe(struct platform_device *pdev)

rcar_i2c_write(priv, ICSAR, 0); /* Gen2: must be 0 if not using slave */

if (priv->devtype < I2C_RCAR_GEN3)
if (priv->devtype < I2C_RCAR_GEN3) {
irqflags |= IRQF_NO_THREAD;
irqhandler = rcar_i2c_gen2_irq;
}

if (priv->devtype == I2C_RCAR_GEN3) {
priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
Expand All @@ -999,7 +1028,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
priv->flags |= ID_P_HOST_NOTIFY;

priv->irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, irqflags, dev_name(dev), priv);
ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv);
if (ret < 0) {
dev_err(dev, "cannot get irq %d\n", priv->irq);
goto out_pm_disable;
Expand Down

0 comments on commit 9c975c4

Please sign in to comment.