Re: [Patch v2 2/4] dmaengine: tegra: Add Tegra GPC DMA driver

From: Jon Hunter
Date: Fri Aug 07 2020 - 12:24:03 EST




On 06/08/2020 08:30, Rajesh Gumasta wrote:
> Adding GPC DMA controller driver for Tegra186 and Tegra194. The driver
> supports dma transfers between memory to memory, IO peripheral to memory
> and memory to IO peripheral.
>
> Signed-off-by: Pavan Kunapuli <pkunapuli@xxxxxxxxxx>
> Signed-off-by: Rajesh Gumasta <rgumasta@xxxxxxxxxx>
> ---
> drivers/dma/Kconfig | 12 +
> drivers/dma/Makefile | 1 +
> drivers/dma/tegra-gpc-dma.c | 1472 +++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1485 insertions(+)
> create mode 100644 drivers/dma/tegra-gpc-dma.c

> +static void tegra_dma_desc_free(struct virt_dma_desc *vd)
> +{
> + struct tegra_dma_desc *dma_desc = vd_to_tegra_dma_desc(vd);
> + struct tegra_dma_channel *tdc = dma_desc->tdc;
> + unsigned long flags;
> +
> + if (!dma_desc)
> + return;

Personally, I would do this the other way around. If dma_desc is valid
then do the below.

> + raw_spin_lock_irqsave(&tdc->lock, flags);
> + kfree(dma_desc);
> + raw_spin_unlock_irqrestore(&tdc->lock, flags);
> +}
> +
> +static int tegra_dma_slave_config(struct dma_chan *dc,
> + struct dma_slave_config *sconfig)
> +{
> + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
> +
> + if (!list_empty(&tdc->pending_sg_req)) {
> + dev_err(tdc2dev(tdc), "Configuration not allowed\n");
> + return -EBUSY;
> + }
> +
> + memcpy(&tdc->dma_sconfig, sconfig, sizeof(*sconfig));
> + if (tdc->slave_id == -1)
> + tdc->slave_id = sconfig->slave_id;
> + tdc->config_init = true;
> + return 0;
> +}
> +
> +static int tegra_dma_pause(struct tegra_dma_channel *tdc)
> +{
> + u32 val;
> + int ret;
> +
> + tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, TEGRA_GPCDMA_CHAN_CSRE_PAUSE);
> +
> + /* Wait until busy bit is de-asserted */
> + ret = readl_relaxed_poll_timeout_atomic(tdc->tdma->base_addr +
> + tdc->chan_base_offset + TEGRA_GPCDMA_CHAN_STATUS,
> + val,
> + !(val & TEGRA_GPCDMA_STATUS_BUSY),
> + TEGRA_GPCDMA_BURST_COMPLETE_TIME,
> + TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT);
> +
> + if (ret) {
> + dev_err(tdc2dev(tdc), "DMA pause timed out\n");
> + return ret;

No need to return here.

> + }
> +
> + return ret;
> +}

...

> +static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
> +{
> + struct tegra_dma_channel *tdc = dev_id;
> + unsigned int err_status;
> + unsigned long status;
> +
> + raw_spin_lock(&tdc->lock);
> +
> + status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_STATUS);
> + err_status = tdc_read(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS);
> +
> + if (err_status) {
> + tegra_dma_chan_decode_error(tdc, err_status);
> + tegra_dma_dump_chan_regs(tdc);
> + tdc_write(tdc, TEGRA_GPCDMA_CHAN_ERR_STATUS, 0xFFFFFFFF);
> + }
> +
> + if (status & TEGRA_GPCDMA_STATUS_ISE_EOC) {
> + tdc_write(tdc, TEGRA_GPCDMA_CHAN_STATUS,
> + TEGRA_GPCDMA_STATUS_ISE_EOC);
> + if (tdc->isr_handler) {
> + tdc->isr_handler(tdc, false);
> + } else {
> + dev_err(tdc->tdma->dev,
> + "GPCDMA CH%d: status %lx ISR handler absent!\n",
> + tdc->id, status);
> + tegra_dma_dump_chan_regs(tdc);
> + }
> + raw_spin_unlock(&tdc->lock);
> + return IRQ_HANDLED;

It is usually better to init a return value to IRQ_NONE at the beginning
and then update the variable here and just have one return point.

> + }
> +
> + raw_spin_unlock(&tdc->lock);
> + return IRQ_NONE;
> +}

...

> +static inline int get_bus_width(struct tegra_dma_channel *tdc,
> + enum dma_slave_buswidth slave_bw)
> +{
> + switch (slave_bw) {
> + case DMA_SLAVE_BUSWIDTH_1_BYTE:
> + return TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_8;
> + case DMA_SLAVE_BUSWIDTH_2_BYTES:
> + return TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_16;
> + case DMA_SLAVE_BUSWIDTH_4_BYTES:
> + return TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_32;
> + case DMA_SLAVE_BUSWIDTH_8_BYTES:
> + return TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_64;
> + default:
> + dev_warn(tdc2dev(tdc),
> + "slave bw is not supported, using 32bits\n");
> + return TEGRA_GPCDMA_MMIOSEQ_BUS_WIDTH_32;

This is an error and so please return an error. I doubtful that there is
any point in trying to continue.

> + }
> +}
> +
> +static inline int get_burst_size(struct tegra_dma_channel *tdc,
> + u32 burst_size,
> + enum dma_slave_buswidth slave_bw,
> + int len)
> +{
> + int burst_mmio_width;
> + int burst_byte;
> +
> + /*
> + * burst_size from client is in terms of the bus_width.
> + * convert that into words.
> + */
> + burst_byte = burst_size * slave_bw;
> + burst_mmio_width = burst_byte / 4;
> +
> + /* If burst size is 0 then calculate the burst size based on length */
> + if (!burst_mmio_width) {
> + if (len & 0xF)

case statement?

> + return TEGRA_GPCDMA_MMIOSEQ_BURST_1;
> + else if ((len >> 3) & 0x1)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_2;
> + else if ((len >> 4) & 0x1)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_4;
> + else if ((len >> 5) & 0x1)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_8;
> + else
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_16;
> + }
> + if (burst_mmio_width < 2)

case statement?

> + return TEGRA_GPCDMA_MMIOSEQ_BURST_1;
> + else if (burst_mmio_width < 4)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_2;
> + else if (burst_mmio_width < 8)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_4;
> + else if (burst_mmio_width < 16)
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_8;
> + else
> + return TEGRA_GPCDMA_MMIOSEQ_BURST_16;
> +}

...

> +static int tegra_dma_probe(struct platform_device *pdev)
> +{
> + const struct tegra_dma_chip_data *cdata = NULL;
> + struct tegra_dma_chip_data *chip_data = NULL;
> + unsigned int nr_chans, stream_id;
> + unsigned int start_chan_idx = 0;
> + struct tegra_dma *tdma;
> + struct resource *res;
> + unsigned int i;
> + int ret;
> +
> + if (pdev->dev.of_node) {

This is always true

> + const struct of_device_id *match;
> +
> + match = of_match_device(of_match_ptr(tegra_dma_of_match),
> + &pdev->dev);
> + if (!match) {

No need to test this.

> + dev_err(&pdev->dev, "Error: No device match found\n");
> + return -ENODEV;
> + }
> + cdata = match->data;
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "dma-channels",
> + &nr_chans);

You appear to have this in two places; one in DT and one in the chip
data. We can use the chip data and so we don't need this.

> + if (!ret) {
> + /* Allocate chip data and update number of channels */
> + chip_data =
> + devm_kzalloc(&pdev->dev,
> + sizeof(struct tegra_dma_chip_data),
> + GFP_KERNEL);
> + if (!chip_data) {
> + dev_err(&pdev->dev, "Error: memory allocation failed\n");
> + return -ENOMEM;
> + }
> + memcpy(chip_data, cdata,
> + sizeof(struct tegra_dma_chip_data));
> + chip_data->nr_channels = nr_chans;
> + cdata = chip_data;
> + }
> + ret = of_property_read_u32(pdev->dev.of_node,
> + "nvidia,start-dma-channel-index",
> + &start_chan_idx);

This is not a valid property for DT. Please remove.

Cheers
Jon

--
nvpublic