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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




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



[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux