diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-sh_mobile.c')
-rw-r--r-- | drivers/i2c/busses/i2c-sh_mobile.c | 112 |
1 files changed, 54 insertions, 58 deletions
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index d7efaf44868b..440d5dbc8b5f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -140,6 +140,7 @@ struct sh_mobile_i2c_data { int sr; bool send_stop; + struct resource *res; struct dma_chan *dma_tx; struct dma_chan *dma_rx; struct scatterlist sg; @@ -539,6 +540,42 @@ static void sh_mobile_i2c_dma_callback(void *data) iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); } +static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, + enum dma_transfer_direction dir, dma_addr_t port_addr) +{ + struct dma_chan *chan; + struct dma_slave_config cfg; + char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; + int ret; + + chan = dma_request_slave_channel_reason(dev, chan_name); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); + return chan; + } + + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port_addr; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + dev_dbg(dev, "got DMA channel for %s\n", chan_name); + return chan; +} + static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) { bool read = pd->msg->flags & I2C_M_RD; @@ -548,7 +585,16 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd) dma_addr_t dma_addr; dma_cookie_t cookie; - if (!chan) + if (PTR_ERR(chan) == -EPROBE_DEFER) { + if (read) + chan = pd->dma_rx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, + pd->res->start + ICDR); + else + chan = pd->dma_tx = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, + pd->res->start + ICDR); + } + + if (IS_ERR(chan)) return; dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir); @@ -747,56 +793,16 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, sh_mobile_i2c_dt_ids); -static int sh_mobile_i2c_request_dma_chan(struct device *dev, enum dma_transfer_direction dir, - dma_addr_t port_addr, struct dma_chan **chan_ptr) -{ - struct dma_chan *chan; - struct dma_slave_config cfg; - char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; - int ret; - - *chan_ptr = NULL; - - chan = dma_request_slave_channel_reason(dev, chan_name); - if (IS_ERR(chan)) { - ret = PTR_ERR(chan); - dev_dbg(dev, "request_channel failed for %s (%d)\n", chan_name, ret); - return ret; - } - - memset(&cfg, 0, sizeof(cfg)); - cfg.direction = dir; - if (dir == DMA_MEM_TO_DEV) { - cfg.dst_addr = port_addr; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - } else { - cfg.src_addr = port_addr; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - } - - ret = dmaengine_slave_config(chan, &cfg); - if (ret) { - dev_dbg(dev, "slave_config failed for %s (%d)\n", chan_name, ret); - dma_release_channel(chan); - return ret; - } - - *chan_ptr = chan; - - dev_dbg(dev, "got DMA channel for %s\n", chan_name); - return 0; -} - static void sh_mobile_i2c_release_dma(struct sh_mobile_i2c_data *pd) { - if (pd->dma_tx) { + if (!IS_ERR(pd->dma_tx)) { dma_release_channel(pd->dma_tx); - pd->dma_tx = NULL; + pd->dma_tx = ERR_PTR(-EPROBE_DEFER); } - if (pd->dma_rx) { + if (!IS_ERR(pd->dma_rx)) { dma_release_channel(pd->dma_rx); - pd->dma_rx = NULL; + pd->dma_rx = ERR_PTR(-EPROBE_DEFER); } } @@ -849,6 +855,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 0); + pd->res = res; pd->reg = devm_ioremap_resource(&dev->dev, res); if (IS_ERR(pd->reg)) return PTR_ERR(pd->reg); @@ -889,17 +896,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) /* Init DMA */ sg_init_table(&pd->sg, 1); pd->dma_direction = DMA_NONE; - ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_DEV_TO_MEM, - res->start + ICDR, &pd->dma_rx); - if (ret == -EPROBE_DEFER) - return ret; - - ret = sh_mobile_i2c_request_dma_chan(pd->dev, DMA_MEM_TO_DEV, - res->start + ICDR, &pd->dma_tx); - if (ret == -EPROBE_DEFER) { - sh_mobile_i2c_release_dma(pd); - return ret; - } + pd->dma_rx = pd->dma_tx = ERR_PTR(-EPROBE_DEFER); /* Enable Runtime PM for this device. * @@ -937,8 +934,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) return ret; } - dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz, DMA=%c\n", - adap->nr, pd->bus_speed, (pd->dma_rx || pd->dma_tx) ? 'y' : 'n'); + dev_info(&dev->dev, "I2C adapter %d, bus speed %lu Hz\n", adap->nr, pd->bus_speed); return 0; } |