Re: [PATCH v7 2/4] dmaengine: tegra: Add tegra gpcdma driver

From: Jon Hunter
Date: Thu Sep 23 2021 - 19:27:34 EST




On 23/09/2021 08:51, Akhil R 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>
Signed-off-by: Akhil R <akhilrajeev@xxxxxxxxxx>
---
drivers/dma/Kconfig | 12 +
drivers/dma/Makefile | 1 +
drivers/dma/tegra186-gpc-dma.c | 1354 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 1367 insertions(+)
create mode 100644 drivers/dma/tegra186-gpc-dma.c

...

+static enum dma_status tegra_dma_tx_status(struct dma_chan *dc,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+ struct tegra_dma_desc *dma_desc = NULL;
+ struct virt_dma_desc *vd;
+ unsigned int residual;
+ enum dma_status ret;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&tdc->lock, flags);
+
+ ret = dma_cookie_status(dc, cookie, txstate);
+ if (ret == DMA_COMPLETE) {
+ raw_spin_unlock_irqrestore(&tdc->lock, flags);
+ return ret;
+ }
+
+ vd = vchan_find_desc(&tdc->vc, cookie);
+ if (vd)
+ dma_desc = vd_to_tegra_dma_desc(vd);

This first case implies that the transfer has not started yet and so the residual is just dma_desc->bytes_requested.

+ else if (tdc->dma_desc && tdc->dma_desc->vd.tx.cookie == cookie)
+ dma_desc = tdc->dma_desc;
+
+ if (dma_desc) {
+ residual = dma_desc->bytes_requested -
+ (dma_desc->bytes_transferred %
+ dma_desc->bytes_requested);
+ dma_set_residue(txstate, residual);
+ } else {
+ dev_err(tdc2dev(tdc), "cookie %d is not found\n", cookie);
+ }
+
+ raw_spin_unlock_irqrestore(&tdc->lock, flags);
+ return ret;
+}
+
+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;
+ default:
+ dev_err(tdc2dev(tdc), "given slave bus width is not supported\n");
+ return -EINVAL;
+ }
+}
+
+static inline int get_burst_size_by_len(int len)
+{
+ int ret;
+
+ /* Get burst size based on the first set bit */
+ switch (ffs(len)) {
+ case 0:
+ case 1:
+ case 2:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_1;
+ break;
+ case 3:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_2;
+ break;
+ case 4:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_4;
+ break;
+ case 5:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_8;
+ break;
+ default:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_16;
+ break;
+ }
+
+ return ret;
+}
+
+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, burst_byte, ret;
+
+ /*
+ * burst_size from client is in terms of the bus_width.
+ * convert that into words.
+ */
+ burst_byte = burst_size * slave_bw;
+ burst_mmio_width = ffs(burst_byte / 4);
+
+ /* Get burst size based on the first set bit */
+ switch (burst_mmio_width) {
+ case 0:
+ ret = get_burst_size_by_len(len);
+ break;
+ case 1:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_1;
+ break;
+ case 2:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_2;
+ break;
+ case 3:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_4;
+ break;
+ case 4:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_8;
+ break;
+ default:
+ ret = TEGRA_GPCDMA_MMIOSEQ_BURST_16;
+ break;
+ }
+
+ return ret;
+}

Something seems a bit odd here in the sense that if burst_mmio_width == 0, we could still end up with a burst of 16? I am not sure I understand this logic.

Jon

--
nvpublic