summaryrefslogtreecommitdiff
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.c191
1 files changed, 86 insertions, 105 deletions
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index c03acdf71397..d856bc211715 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -40,21 +40,21 @@
/* BUS: S A8 ACK P(*) */
/* IRQ: DTE WAIT */
/* ICIC: */
-/* ICCR: 0x94 0x90 */
+/* ICCR: 0x94 0x90 */
/* ICDR: A8 */
/* */
/* 1 byte transmit */
/* BUS: S A8 ACK D8(1) ACK P(*) */
/* IRQ: DTE WAIT WAIT */
/* ICIC: -DTE */
-/* ICCR: 0x94 0x90 */
+/* ICCR: 0x94 0x90 */
/* ICDR: A8 D8(1) */
/* */
/* 2 byte transmit */
/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */
/* IRQ: DTE WAIT WAIT WAIT */
/* ICIC: -DTE */
-/* ICCR: 0x94 0x90 */
+/* ICCR: 0x94 0x90 */
/* ICDR: A8 D8(1) D8(2) */
/* */
/* 3 bytes or more, +---------+ gets repeated */
@@ -113,7 +113,6 @@ enum sh_mobile_i2c_op {
OP_TX_FIRST,
OP_TX,
OP_TX_STOP,
- OP_TX_STOP_DATA,
OP_TX_TO_RX,
OP_RX,
OP_RX_STOP,
@@ -145,11 +144,12 @@ struct sh_mobile_i2c_data {
struct dma_chan *dma_rx;
struct scatterlist sg;
enum dma_data_direction dma_direction;
+ u8 *dma_buf;
};
struct sh_mobile_dt_config {
int clks_per_count;
- void (*setup)(struct sh_mobile_i2c_data *pd);
+ int (*setup)(struct sh_mobile_i2c_data *pd);
};
#define IIC_FLAG_HAS_ICIC67 (1 << 0)
@@ -246,36 +246,10 @@ static u32 sh_mobile_i2c_icch(unsigned long count_khz, u32 tHIGH, u32 tf)
return (((count_khz * (tHIGH + tf)) + 5000) / 10000);
}
-static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
+static int sh_mobile_i2c_check_timing(struct sh_mobile_i2c_data *pd)
{
- unsigned long i2c_clk_khz;
- u32 tHIGH, tLOW, tf;
- uint16_t max_val;
-
- /* Get clock rate after clock is enabled */
- clk_prepare_enable(pd->clk);
- i2c_clk_khz = clk_get_rate(pd->clk) / 1000;
- clk_disable_unprepare(pd->clk);
- i2c_clk_khz /= pd->clks_per_count;
-
- if (pd->bus_speed == STANDARD_MODE) {
- tLOW = 47; /* tLOW = 4.7 us */
- tHIGH = 40; /* tHD;STA = tHIGH = 4.0 us */
- tf = 3; /* tf = 0.3 us */
- } else if (pd->bus_speed == FAST_MODE) {
- tLOW = 13; /* tLOW = 1.3 us */
- tHIGH = 6; /* tHD;STA = tHIGH = 0.6 us */
- tf = 3; /* tf = 0.3 us */
- } else {
- dev_err(pd->dev, "unrecognized bus speed %lu Hz\n",
- pd->bus_speed);
- return -EINVAL;
- }
-
- pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf);
- pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf);
+ u16 max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff;
- max_val = pd->flags & IIC_FLAG_HAS_ICIC67 ? 0x1ff : 0xff;
if (pd->iccl > max_val || pd->icch > max_val) {
dev_err(pd->dev, "timing values out of range: L/H=0x%x/0x%x\n",
pd->iccl, pd->icch);
@@ -298,35 +272,43 @@ static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
return 0;
}
-static void activate_ch(struct sh_mobile_i2c_data *pd)
+static int sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
{
- /* Wake up device and enable clock */
- pm_runtime_get_sync(pd->dev);
- clk_prepare_enable(pd->clk);
+ unsigned long i2c_clk_khz;
+ u32 tHIGH, tLOW, tf;
- /* Enable channel and configure rx ack */
- iic_set_clr(pd, ICCR, ICCR_ICE, 0);
+ i2c_clk_khz = clk_get_rate(pd->clk) / 1000 / pd->clks_per_count;
- /* Mask all interrupts */
- iic_wr(pd, ICIC, 0);
+ if (pd->bus_speed == STANDARD_MODE) {
+ tLOW = 47; /* tLOW = 4.7 us */
+ tHIGH = 40; /* tHD;STA = tHIGH = 4.0 us */
+ tf = 3; /* tf = 0.3 us */
+ } else if (pd->bus_speed == FAST_MODE) {
+ tLOW = 13; /* tLOW = 1.3 us */
+ tHIGH = 6; /* tHD;STA = tHIGH = 0.6 us */
+ tf = 3; /* tf = 0.3 us */
+ } else {
+ dev_err(pd->dev, "unrecognized bus speed %lu Hz\n",
+ pd->bus_speed);
+ return -EINVAL;
+ }
- /* Set the clock */
- iic_wr(pd, ICCL, pd->iccl & 0xff);
- iic_wr(pd, ICCH, pd->icch & 0xff);
+ pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf);
+ pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf);
+
+ return sh_mobile_i2c_check_timing(pd);
}
-static void deactivate_ch(struct sh_mobile_i2c_data *pd)
+static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd)
{
- /* Clear/disable interrupts */
- iic_wr(pd, ICSR, 0);
- iic_wr(pd, ICIC, 0);
+ unsigned long clks_per_cycle;
- /* Disable channel */
- iic_set_clr(pd, ICCR, 0, ICCR_ICE);
+ /* L = 5, H = 4, L + H = 9 */
+ clks_per_cycle = clk_get_rate(pd->clk) / pd->bus_speed;
+ pd->iccl = DIV_ROUND_UP(clks_per_cycle * 5 / 9 - 1, pd->clks_per_count);
+ pd->icch = DIV_ROUND_UP(clks_per_cycle * 4 / 9 - 5, pd->clks_per_count);
- /* Disable clock and mark device as idle */
- clk_disable_unprepare(pd->clk);
- pm_runtime_put_sync(pd->dev);
+ return sh_mobile_i2c_check_timing(pd);
}
static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
@@ -350,10 +332,7 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
case OP_TX: /* write data */
iic_wr(pd, ICDR, data);
break;
- case OP_TX_STOP_DATA: /* write data and issue a stop afterwards */
- iic_wr(pd, ICDR, data);
- /* fallthrough */
- case OP_TX_STOP: /* issue a stop */
+ case OP_TX_STOP: /* issue a stop (or rep_start) */
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
: ICCR_ICE | ICCR_TRS | ICCR_BBSY);
break;
@@ -387,11 +366,6 @@ static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)
return pd->pos == -1;
}
-static bool sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd)
-{
- return pd->pos == pd->msg->len - 1;
-}
-
static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
unsigned char *buf)
{
@@ -409,20 +383,12 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
unsigned char data;
if (pd->pos == pd->msg->len) {
- /* Send stop if we haven't yet (DMA case) */
- if (pd->send_stop && pd->stop_after_dma)
- i2c_op(pd, OP_TX_STOP, 0);
+ i2c_op(pd, OP_TX_STOP, 0);
return 1;
}
sh_mobile_i2c_get_data(pd, &data);
-
- if (sh_mobile_i2c_is_last_byte(pd))
- i2c_op(pd, OP_TX_STOP_DATA, data);
- else if (sh_mobile_i2c_is_first_byte(pd))
- i2c_op(pd, OP_TX_FIRST, data);
- else
- i2c_op(pd, OP_TX, data);
+ i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data);
pd->pos++;
return 0;
@@ -464,8 +430,9 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
break;
}
data = i2c_op(pd, OP_RX_STOP_DATA, 0);
- } else
+ } else if (real_pos >= 0) {
data = i2c_op(pd, OP_RX, 0);
+ }
if (real_pos >= 0)
pd->msg->buf[real_pos] = data;
@@ -548,6 +515,8 @@ static void sh_mobile_i2c_dma_callback(void *data)
pd->pos = pd->msg->len;
pd->stop_after_dma = true;
+ i2c_release_dma_safe_msg_buf(pd->msg, pd->dma_buf);
+
iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
}
@@ -608,7 +577,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
if (IS_ERR(chan))
return;
- dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir);
+ dma_addr = dma_map_single(chan->device->dev, pd->dma_buf, pd->msg->len, dir);
if (dma_mapping_error(chan->device->dev, dma_addr)) {
dev_dbg(pd->dev, "dma map failed, using PIO\n");
return;
@@ -651,10 +620,10 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
if (do_init) {
/* Initialize channel registers */
- iic_set_clr(pd, ICCR, 0, ICCR_ICE);
+ iic_wr(pd, ICCR, ICCR_SCP);
/* Enable channel and configure rx ack */
- iic_set_clr(pd, ICCR, ICCR_ICE, 0);
+ iic_wr(pd, ICCR, ICCR_ICE | ICCR_SCP);
/* Set the clock */
iic_wr(pd, ICCL, pd->iccl & 0xff);
@@ -665,7 +634,8 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
pd->pos = -1;
pd->sr = 0;
- if (pd->msg->len > 8)
+ pd->dma_buf = i2c_get_dma_safe_msg_buf(pd->msg, 8);
+ if (pd->dma_buf)
sh_mobile_i2c_xfer_dma(pd);
/* Enable all interrupts to begin with */
@@ -731,7 +701,8 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
int i;
long timeout;
- activate_ch(pd);
+ /* Wake up device and enable clock */
+ pm_runtime_get_sync(pd->dev);
/* Process all messages */
for (i = 0; i < num; i++) {
@@ -768,11 +739,13 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
break;
}
- deactivate_ch(pd);
+ /* Disable channel */
+ iic_wr(pd, ICCR, ICCR_SCP);
- if (!err)
- err = num;
- return err;
+ /* Disable clock and mark device as idle */
+ pm_runtime_put_sync(pd->dev);
+
+ return err ?: num;
}
static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter)
@@ -789,7 +762,7 @@ static const struct i2c_algorithm sh_mobile_i2c_algorithm = {
* 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)
+static int 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 */
@@ -810,14 +783,23 @@ static void sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
udelay(10);
iic_wr(pd, ICCR, ICCR_TRS);
udelay(10);
+
+ return sh_mobile_i2c_init(pd);
}
static const struct sh_mobile_dt_config default_dt_config = {
.clks_per_count = 1,
+ .setup = sh_mobile_i2c_init,
};
static const struct sh_mobile_dt_config fast_clock_dt_config = {
.clks_per_count = 2,
+ .setup = sh_mobile_i2c_init,
+};
+
+static const struct sh_mobile_dt_config v2_freq_calc_dt_config = {
+ .clks_per_count = 2,
+ .setup = sh_mobile_i2c_v2_init,
};
static const struct sh_mobile_dt_config r8a7740_dt_config = {
@@ -828,7 +810,7 @@ static const struct sh_mobile_dt_config r8a7740_dt_config = {
static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
{ .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-r8a7790", .data = &v2_freq_calc_dt_config },
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
@@ -910,32 +892,13 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
return PTR_ERR(pd->reg);
ret = of_property_read_u32(dev->dev.of_node, "clock-frequency", &bus_speed);
- pd->bus_speed = ret ? STANDARD_MODE : bus_speed;
+ pd->bus_speed = (ret || !bus_speed) ? STANDARD_MODE : bus_speed;
pd->clks_per_count = 1;
- config = of_device_get_match_data(&dev->dev);
- if (config) {
- pd->clks_per_count = config->clks_per_count;
-
- if (config->setup)
- config->setup(pd);
- }
-
- /* The IIC blocks on SH-Mobile ARM processors
- * come with two new bits in ICIC.
- */
+ /* Newer variants come with two new bits in ICIC */
if (resource_size(res) > 0x17)
pd->flags |= IIC_FLAG_HAS_ICIC67;
- ret = sh_mobile_i2c_init(pd);
- if (ret)
- return ret;
-
- /* Init DMA */
- sg_init_table(&pd->sg, 1);
- pd->dma_direction = DMA_NONE;
- pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER);
-
/* Enable Runtime PM for this device.
*
* Also tell the Runtime PM core to ignore children
@@ -948,6 +911,24 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
*/
pm_suspend_ignore_children(&dev->dev, true);
pm_runtime_enable(&dev->dev);
+ pm_runtime_get_sync(&dev->dev);
+
+ config = of_device_get_match_data(&dev->dev);
+ if (config) {
+ pd->clks_per_count = config->clks_per_count;
+ ret = config->setup(pd);
+ } else {
+ ret = sh_mobile_i2c_init(pd);
+ }
+
+ pm_runtime_put_sync(&dev->dev);
+ if (ret)
+ return ret;
+
+ /* Init DMA */
+ sg_init_table(&pd->sg, 1);
+ pd->dma_direction = DMA_NONE;
+ pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER);
/* setup the private data */
adap = &pd->adap;