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 <pkunap...@nvidia.com>
> Signed-off-by: Rajesh Gumasta <rguma...@nvidia.com>
> ---
>  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

Reply via email to