diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-sh_mobile.c')
-rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 191 |
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; |