aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-sh_mobile.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-sh_mobile.c')
-rw-r--r--drivers/i2c/busses/i2c-sh_mobile.c49
1 files changed, 45 insertions, 4 deletions
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 007818b3e174..47659a925e09 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -150,6 +150,7 @@ struct sh_mobile_i2c_data {
struct sh_mobile_dt_config {
int clks_per_count;
+ void (*setup)(struct sh_mobile_i2c_data *pd);
};
#define IIC_FLAG_HAS_ICIC67 (1 << 0)
@@ -164,6 +165,7 @@ struct sh_mobile_dt_config {
#define ICIC 0x0c
#define ICCL 0x10
#define ICCH 0x14
+#define ICSTART 0x70
/* Register bits */
#define ICCR_ICE 0x80
@@ -190,6 +192,8 @@ struct sh_mobile_dt_config {
#define ICIC_WAITE 0x02
#define ICIC_DTEE 0x01
+#define ICSTART_ICSTART 0x10
+
static void iic_wr(struct sh_mobile_i2c_data *pd, int offs, unsigned char data)
{
if (offs == ICIC)
@@ -726,7 +730,8 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter);
struct i2c_msg *msg;
int err = 0;
- int i, k;
+ int i;
+ long timeout;
activate_ch(pd);
@@ -745,10 +750,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
i2c_op(pd, OP_START, 0);
/* The interrupt handler takes care of the rest... */
- k = wait_event_timeout(pd->wait,
+ timeout = wait_event_timeout(pd->wait,
pd->sr & (ICSR_TACK | SW_DONE),
- 5 * HZ);
- if (!k) {
+ adapter->timeout);
+ if (!timeout) {
dev_err(pd->dev, "Transfer request timed out\n");
if (pd->dma_direction != DMA_NONE)
sh_mobile_i2c_cleanup_dma(pd);
@@ -782,6 +787,33 @@ static struct i2c_algorithm sh_mobile_i2c_algorithm = {
.master_xfer = sh_mobile_i2c_xfer,
};
+/*
+ * r8a7740 chip has lasting errata on I2C I/O pad reset.
+ * this is work-around for it.
+ */
+static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
+{
+ iic_set_clr(pd, ICCR, ICCR_ICE, 0);
+ iic_rd(pd, ICCR); /* dummy read */
+
+ iic_set_clr(pd, ICSTART, ICSTART_ICSTART, 0);
+ iic_rd(pd, ICSTART); /* dummy read */
+
+ udelay(10);
+
+ iic_wr(pd, ICCR, ICCR_SCP);
+ iic_wr(pd, ICSTART, 0);
+
+ udelay(10);
+
+ iic_wr(pd, ICCR, ICCR_TRS);
+ udelay(10);
+ iic_wr(pd, ICCR, 0);
+ udelay(10);
+ iic_wr(pd, ICCR, ICCR_TRS);
+ udelay(10);
+}
+
static const struct sh_mobile_dt_config default_dt_config = {
.clks_per_count = 1,
};
@@ -790,9 +822,15 @@ static const struct sh_mobile_dt_config fast_clock_dt_config = {
.clks_per_count = 2,
};
+static const struct sh_mobile_dt_config r8a7740_dt_config = {
+ .clks_per_count = 1,
+ .setup = sh_mobile_i2c_r8a7740_workaround,
+};
+
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
{ .compatible = "renesas,iic-r8a73a4", .data = &fast_clock_dt_config },
+ { .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
{ .compatible = "renesas,iic-r8a7790", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
@@ -885,6 +923,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
config = match->data;
pd->clks_per_count = config->clks_per_count;
+
+ if (config->setup)
+ config->setup(pd);
}
} else {
if (pdata && pdata->bus_speed)