diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-rcar.c')
-rw-r--r-- | drivers/i2c/busses/i2c-rcar.c | 62 |
1 files changed, 56 insertions, 6 deletions
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 15d764afec3b..4159ebcec2bb 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -62,7 +62,7 @@ #define MIE (1 << 3) /* master if enable */ #define TSBE (1 << 2) #define FSB (1 << 1) /* force stop bit */ -#define ESG (1 << 0) /* en startbit gen */ +#define ESG (1 << 0) /* enable start bit gen */ /* ICSSR (also for ICSIER) */ #define GCAR (1 << 6) /* general call received */ @@ -132,6 +132,7 @@ struct rcar_i2c_priv { int pos; u32 icccr; u32 flags; + u8 recovery_icmcr; /* protected by adapter lock */ enum rcar_i2c_type devtype; struct i2c_client *slave; @@ -158,6 +159,46 @@ static u32 rcar_i2c_read(struct rcar_i2c_priv *priv, int reg) return readl(priv->io + reg); } +static int rcar_i2c_get_scl(struct i2c_adapter *adap) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + return !!(rcar_i2c_read(priv, ICMCR) & FSCL); + +}; + +static void rcar_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + if (val) + priv->recovery_icmcr |= FSCL; + else + priv->recovery_icmcr &= ~FSCL; + + rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr); +}; + +/* No get_sda, because the HW only reports its bus free logic, not SDA itself */ + +static void rcar_i2c_set_sda(struct i2c_adapter *adap, int val) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + + if (val) + priv->recovery_icmcr |= FSDA; + else + priv->recovery_icmcr &= ~FSDA; + + rcar_i2c_write(priv, ICMCR, priv->recovery_icmcr); +}; + +static struct i2c_bus_recovery_info rcar_i2c_bri = { + .get_scl = rcar_i2c_get_scl, + .set_scl = rcar_i2c_set_scl, + .set_sda = rcar_i2c_set_sda, + .recover_bus = i2c_generic_scl_recovery, +}; static void rcar_i2c_init(struct rcar_i2c_priv *priv) { /* reset master mode */ @@ -170,7 +211,7 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv) static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) { - int i; + int i, ret; for (i = 0; i < LOOP_TIMEOUT; i++) { /* make sure that bus is not busy */ @@ -179,7 +220,15 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv) udelay(1); } - return -EBUSY; + /* Waiting did not help, try to recover */ + priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL; + ret = i2c_recover_bus(&priv->adap); + + /* No failure when recovering, so check bus busy bit again */ + if (ret == 0) + ret = (rcar_i2c_read(priv, ICMCR) & FSDA) ? -EBUSY : 0; + + return ret; } static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t) @@ -282,7 +331,7 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read); /* - * We don't have a testcase but the HW engineers say that the write order + * We don't have a test case but the HW engineers say that the write order * of ICMSR and ICMCR depends on whether we issue START or REP_START. Since * it didn't cause a drawback for me, let's rather be safe than sorry. */ @@ -359,7 +408,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv) int len; /* Do not use DMA if it's not available or for messages < 8 bytes */ - if (IS_ERR(chan) || msg->len < 8) + if (IS_ERR(chan) || msg->len < 8 || !(msg->flags & I2C_M_DMA_SAFE)) return; if (read) { @@ -440,7 +489,7 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr) /* * Try to use DMA to transmit the rest of the data if - * address transfer pashe just finished. + * address transfer phase just finished. */ if (msr & MAT) rcar_i2c_dma(priv); @@ -851,6 +900,7 @@ static int rcar_i2c_probe(struct platform_device *pdev) adap->retries = 3; adap->dev.parent = dev; adap->dev.of_node = dev->of_node; + adap->bus_recovery_info = &rcar_i2c_bri; i2c_set_adapdata(adap, priv); strlcpy(adap->name, pdev->name, sizeof(adap->name)); |