RE: [PATCH V8 3/5] i2c: tegra: Add DMA Support

From: Sowjanya Komatineni
Date: Thu Jan 31 2019 - 11:41:11 EST


> > +static int tegra_i2c_init_dma_param(struct tegra_i2c_dev *i2c_dev) {
> > + struct dma_chan *dma_chan;
> > + u32 *dma_buf;
> > + dma_addr_t dma_phys;
> > +
> > + if (!i2c_dev->has_dma)
> > + return -EINVAL;
> > +
> > + if (!i2c_dev->rx_dma_chan) {
> > + dma_chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx");
> > + if (IS_ERR(dma_chan))
> > + return PTR_ERR(dma_chan);
>
> I think we want to fallback to PIO here if dma_chan is -ENODEV.

Checking if dmas property is in device tree before channel requests. So ENODEV will not be returned.
Incase if dmas property is not there in device tree, we fall back to PIO as init_dma_params returns invalid

> Although, I'm not exactly sure I understand what you're trying to achieve here. Shouldn't we move the channel request parts into probe and remove them from here? Otherwise it seems like we could get into a state where we keep trying to get the slave channels everytime we set up a DMA transfer, even if we already failed to do so during probe.
>

This patch tries to get channel request during probe but buffer allocation will not happen till xfer is needed in dma mode.
During xfer message, this function is called again for dma buffer allocation only for dma mode.

> > +
> > + if (!i2c_dev->dma_buf && i2c_dev->msg_buf_remaining) {
> > + dma_buf = dma_alloc_coherent(i2c_dev->dev,
> > + i2c_dev->dma_buf_size,
> > + &dma_phys, GFP_KERNEL);
> > +
> > + if (!dma_buf) {
> > + dev_err(i2c_dev->dev,
> > + "failed to allocate the DMA buffer\n");
> > + dma_release_channel(i2c_dev->tx_dma_chan);
> > + dma_release_channel(i2c_dev->rx_dma_chan);
> > + i2c_dev->tx_dma_chan = NULL;
> > + i2c_dev->rx_dma_chan = NULL;
> > + return -ENOMEM;
> > + }
> > +
> > + i2c_dev->dma_buf = dma_buf;
> > + i2c_dev->dma_phys = dma_phys;
> > + }
> > +
> > + return 0;
> > +}
> > +
>
>
> > @@ -749,19 +939,69 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
> > i2c_dev->msg_read = (msg->flags & I2C_M_RD);
> > reinit_completion(&i2c_dev->msg_complete);
> >
> > + if (i2c_dev->msg_read)
> > + xfer_size = msg->len;
> > + else
> > + xfer_size = msg->len + I2C_PACKET_HEADER_SIZE;
> > +
> > + xfer_size = ALIGN(xfer_size, BYTES_PER_FIFO_WORD);
> > + dma = (xfer_size > I2C_PIO_MODE_MAX_LEN);
> > + if (dma) {
> > + err = tegra_i2c_init_dma_param(i2c_dev);
> > + if (err < 0) {
> > + dev_dbg(i2c_dev->dev, "switching to PIO transfer\n");
> > + dma = false;
> > + }
>
>
> If we successfully got DMA channels at probe time, doesn't this turn into an error condition that is worth reporting? It seems to me like the only reason it could fail is if we fail the allocation, but then again, why don't we move the DMA buffer allocation into probe()? We already use a fixed size for that allocation, so there's no reason it couldn't be allocated at probe time.
>
> Seems like maybe you just overlooked that as you were moving around the code pieces.

Checking for dmas property is inside init_dma_param and if dmas property doesn't exist init_dma_param returns err EINVAL
Also init_dma_param fails for if buffer allocation fails. In both of those cases it will switch to PIO Mode
As per review feedback, performing channel request allocation during probe and buffer allocation is postponed till there is need for DMA and buffer allocation happens during 1st DMA mode xfer if it is not already allocated

>
>
>
> > static const struct i2c_algorithm tegra_i2c_algo = { @@ -1002,11
> > +1293,13 @@ static int tegra_i2c_probe(struct platform_device *pdev)
> > struct clk *div_clk;
> > struct clk *fast_clk;
> > void __iomem *base;
> > + phys_addr_t base_phys;
> > int irq;
> > int ret = 0;
> > int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE;
> >
> > res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + base_phys = res->start;
> > base = devm_ioremap_resource(&pdev->dev, res);
> > if (IS_ERR(base))
> > return PTR_ERR(base);
> > @@ -1029,6 +1322,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
> > return -ENOMEM;
> >
> > i2c_dev->base = base;
> > + i2c_dev->base_phys = base_phys;
>
> We could probably do without the extra local variable by just moving the assignment here. res is still valid at this point.
Same res is used for both IORESOURCE_MEM ad IORESOURCE_IRQ and i2c_dev allocation happens later so I had to use temp variable