[RFC PATCH 2/7] DMA: tegra-apb: Move code dealing with h/w registers into separate functions

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

 



In preparation for adding the Tegra210 ADMA driver, that is based upon the
Tegra20-APB DMA driver, move code that accesses hardware registers into
specific functions. The Tegra210 ADMA and Tegra20-APB DMA drivers are not
compatible from a hardware register perspective, but the drivers are very
much the same. Hence, by isolating code that deals with the hardware
registers it will then be possible to add a function table to call code
that accesses the hardware registers and re-use the common driver code
for both DMAs.

Signed-off-by: Jon Hunter <jonathanh@xxxxxxxxxx>
---
 drivers/dma/tegra20-apb-dma.c | 277 ++++++++++++++++++++++++++----------------
 1 file changed, 170 insertions(+), 107 deletions(-)

diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 097432ea89fa..e552a4efef71 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -359,6 +359,18 @@ static int tegra_dma_slave_config(struct dma_chan *dc,
 	return 0;
 }
 
+static u32 tegra_dma_get_xfer_count(struct tegra_dma_channel *tdc)
+{
+	u32 wcount;
+
+	if (tdc->tdma->chip_data->support_separate_wcount_reg)
+		wcount = tdc_read(tdc, TEGRA_APBDMA_CHAN_WORD_TRANSFER);
+	else
+		wcount = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
+
+	return (wcount & TEGRA_APBDMA_STATUS_COUNT_MASK) - 4;
+}
+
 static void tegra_dma_global_pause(struct tegra_dma_channel *tdc,
 	bool wait_for_burst_complete)
 {
@@ -394,6 +406,38 @@ out:
 	spin_unlock(&tdma->global_lock);
 }
 
+static u32 tegra_dma_irq_status(struct tegra_dma_channel *tdc)
+{
+	u32 status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
+
+	return status & TEGRA_APBDMA_STATUS_ISE_EOC;
+}
+
+static u32 tegra_dma_irq_clear(struct tegra_dma_channel *tdc)
+{
+	u32 status = tegra_dma_irq_status(tdc);
+
+	if (status) {
+		dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__);
+		tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status);
+	}
+
+	return status;
+}
+
+static void tegra_dma_program(struct tegra_dma_channel *tdc,
+			      struct tegra_dma_sg_req *nsg_req)
+{
+	tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, nsg_req->ch_regs.apb_ptr);
+	tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, nsg_req->ch_regs.ahb_ptr);
+	if (tdc->tdma->chip_data->support_separate_wcount_reg)
+		tdc_write(tdc, TEGRA_APBDMA_CHAN_WCOUNT,
+			  nsg_req->ch_regs.wcount);
+	tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR,
+		  nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB);
+	nsg_req->configured = true;
+}
+
 static void tegra_dma_pause(struct tegra_dma_channel *tdc,
 	bool wait_for_burst_complete)
 {
@@ -423,7 +467,6 @@ static void tegra_dma_resume(struct tegra_dma_channel *tdc)
 static void tegra_dma_stop(struct tegra_dma_channel *tdc)
 {
 	u32 csr;
-	u32 status;
 
 	/* Disable interrupts */
 	csr = tdc_read(tdc, TEGRA_APBDMA_CHAN_CSR);
@@ -435,11 +478,8 @@ static void tegra_dma_stop(struct tegra_dma_channel *tdc)
 	tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, csr);
 
 	/* Clear interrupt status if it is there */
-	status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
-	if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
-		dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__);
-		tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status);
-	}
+	tegra_dma_irq_clear(tdc);
+
 	tdc->busy = false;
 }
 
@@ -478,13 +518,13 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc,
 	 * load new configuration.
 	 */
 	tegra_dma_pause(tdc, false);
-	status  = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
+	status = tegra_dma_irq_status(tdc);
 
 	/*
 	 * If interrupt is pending then do nothing as the ISR will handle
 	 * the programing for new request.
 	 */
-	if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
+	if (status) {
 		dev_err(tdc2dev(tdc),
 			"Skipping new configuration as interrupt is pending\n");
 		tegra_dma_resume(tdc);
@@ -492,15 +532,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc,
 	}
 
 	/* Safe to program new configuration */
-	tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, nsg_req->ch_regs.apb_ptr);
-	tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, nsg_req->ch_regs.ahb_ptr);
-	if (tdc->tdma->chip_data->support_separate_wcount_reg)
-		tdc_write(tdc, TEGRA_APBDMA_CHAN_WCOUNT,
-						nsg_req->ch_regs.wcount);
-	tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR,
-				nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB);
-	nsg_req->configured = true;
-
+	tegra_dma_program(tdc, nsg_req);
 	tegra_dma_resume(tdc);
 }
 
@@ -534,10 +566,10 @@ static void tdc_configure_next_head_desc(struct tegra_dma_channel *tdc)
 	}
 }
 
-static inline int get_current_xferred_count(struct tegra_dma_channel *tdc,
-	struct tegra_dma_sg_req *sg_req, unsigned long status)
+static inline int get_current_xferred_count(struct tegra_dma_sg_req *sg_req,
+					    unsigned long wcount)
 {
-	return sg_req->req_len - (status & TEGRA_APBDMA_STATUS_COUNT_MASK) - 4;
+	return sg_req->req_len - wcount;
 }
 
 static void tegra_dma_abort_all(struct tegra_dma_channel *tdc)
@@ -683,9 +715,8 @@ static irqreturn_t tegra_dma_isr(int irq, void *dev_id)
 
 	spin_lock_irqsave(&tdc->lock, flags);
 
-	status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
-	if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
-		tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status);
+	status = tegra_dma_irq_clear(tdc);
+	if (status) {
 		tdc->isr_handler(tdc, false);
 		tasklet_schedule(&tdc->tasklet);
 		spin_unlock_irqrestore(&tdc->lock, flags);
@@ -762,16 +793,13 @@ static int tegra_dma_terminate_all(struct dma_chan *dc)
 	/* Pause DMA before checking the queue status */
 	tegra_dma_pause(tdc, true);
 
-	status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
-	if (status & TEGRA_APBDMA_STATUS_ISE_EOC) {
+	status = tegra_dma_irq_status(tdc);
+	if (status) {
 		dev_dbg(tdc2dev(tdc), "%s():handling isr\n", __func__);
 		tdc->isr_handler(tdc, true);
-		status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS);
 	}
-	if (tdc->tdma->chip_data->support_separate_wcount_reg)
-		wcount = tdc_read(tdc, TEGRA_APBDMA_CHAN_WORD_TRANSFER);
-	else
-		wcount = status;
+
+	wcount = tegra_dma_get_xfer_count(tdc);
 
 	was_busy = tdc->busy;
 	tegra_dma_stop(tdc);
@@ -780,7 +808,7 @@ static int tegra_dma_terminate_all(struct dma_chan *dc)
 		sgreq = list_first_entry(&tdc->pending_sg_req,
 					typeof(*sgreq), node);
 		sgreq->dma_desc->bytes_transferred +=
-				get_current_xferred_count(tdc, sgreq, wcount);
+				get_current_xferred_count(sgreq, wcount);
 	}
 	tegra_dma_resume(tdc);
 
@@ -865,11 +893,27 @@ static inline int get_bus_width(struct tegra_dma_channel *tdc,
 }
 
 static inline int get_burst_size(struct tegra_dma_channel *tdc,
-	u32 burst_size, enum dma_slave_buswidth slave_bw, int len)
+				 enum dma_transfer_direction direction,
+				 int len)
 {
+	enum dma_slave_buswidth slave_bw;
+	u32 burst_size;
 	int burst_byte;
 	int burst_ahb_width;
 
+	switch (direction) {
+	case DMA_MEM_TO_DEV:
+		burst_size = tdc->dma_sconfig.dst_maxburst;
+		slave_bw = tdc->dma_sconfig.dst_addr_width;
+		break;
+	case DMA_DEV_TO_MEM:
+		burst_size = tdc->dma_sconfig.src_maxburst;
+		slave_bw = tdc->dma_sconfig.src_addr_width;
+		break;
+	default:
+		return TEGRA_APBDMA_AHBSEQ_BURST_1;
+	}
+
 	/*
 	 * burst_size from client is in terms of the bus_width.
 	 * convert them into AHB memory width which is 4 byte.
@@ -894,29 +938,23 @@ static inline int get_burst_size(struct tegra_dma_channel *tdc,
 		return TEGRA_APBDMA_AHBSEQ_BURST_8;
 }
 
-static int get_transfer_param(struct tegra_dma_channel *tdc,
-	enum dma_transfer_direction direction, unsigned long *apb_addr,
-	unsigned long *apb_seq,	unsigned long *csr, unsigned int *burst_size,
-	enum dma_slave_buswidth *slave_bw)
+static int tegra_dma_get_xfer_params(struct tegra_dma_channel *tdc,
+				     struct tegra_dma_channel_regs *ch_regs,
+				     enum dma_transfer_direction direction)
 {
-
 	switch (direction) {
 	case DMA_MEM_TO_DEV:
-		*apb_addr = tdc->dma_sconfig.dst_addr;
-		*apb_seq = get_bus_width(tdc, tdc->dma_sconfig.dst_addr_width);
-		*burst_size = tdc->dma_sconfig.dst_maxburst;
-		*slave_bw = tdc->dma_sconfig.dst_addr_width;
-		*csr = TEGRA_APBDMA_CSR_DIR;
+		ch_regs->apb_ptr = tdc->dma_sconfig.dst_addr;
+		ch_regs->apb_seq = get_bus_width(tdc,
+					tdc->dma_sconfig.dst_addr_width);
+		ch_regs->csr = TEGRA_APBDMA_CSR_DIR;
 		return 0;
-
 	case DMA_DEV_TO_MEM:
-		*apb_addr = tdc->dma_sconfig.src_addr;
-		*apb_seq = get_bus_width(tdc, tdc->dma_sconfig.src_addr_width);
-		*burst_size = tdc->dma_sconfig.src_maxburst;
-		*slave_bw = tdc->dma_sconfig.src_addr_width;
-		*csr = 0;
+		ch_regs->apb_ptr = tdc->dma_sconfig.src_addr;
+		ch_regs->apb_seq = get_bus_width(tdc,
+					tdc->dma_sconfig.src_addr_width);
+		ch_regs->csr = 0;
 		return 0;
-
 	default:
 		dev_err(tdc2dev(tdc), "Dma direction is not supported\n");
 		return -EINVAL;
@@ -924,6 +962,60 @@ static int get_transfer_param(struct tegra_dma_channel *tdc,
 	return -EINVAL;
 }
 
+static int tegra_dma_get_xfer_params_sg(struct tegra_dma_channel *tdc,
+					struct tegra_dma_sg_req *sg_req,
+					enum dma_transfer_direction direction,
+					unsigned int flags)
+{
+	struct tegra_dma_channel_regs *ch_regs = &sg_req->ch_regs;
+	int ret;
+
+	ret = tegra_dma_get_xfer_params(tdc, ch_regs, direction);
+	if (ret < 0)
+		return ret;
+
+	ch_regs->ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB;
+	ch_regs->ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE <<
+					TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
+	ch_regs->ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32;
+
+	ch_regs->csr |= TEGRA_APBDMA_CSR_ONCE | TEGRA_APBDMA_CSR_FLOW;
+	ch_regs->csr |= tdc->slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT;
+	if (flags & DMA_PREP_INTERRUPT)
+		ch_regs->csr |= TEGRA_APBDMA_CSR_IE_EOC;
+
+	ch_regs->apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1;
+
+	return 0;
+}
+
+static int tegra_dma_get_xfer_params_cyclic(struct tegra_dma_channel *tdc,
+					struct tegra_dma_sg_req *sg_req,
+					enum dma_transfer_direction direction,
+					unsigned int flags)
+{
+	struct tegra_dma_channel_regs *ch_regs = &sg_req->ch_regs;
+	int ret;
+
+	ret = tegra_dma_get_xfer_params(tdc, ch_regs, direction);
+	if (ret < 0)
+		return ret;
+
+	ch_regs->ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB;
+	ch_regs->ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE <<
+					TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
+	ch_regs->ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32;
+
+	ch_regs->csr |= TEGRA_APBDMA_CSR_FLOW;
+	if (flags & DMA_PREP_INTERRUPT)
+		ch_regs->csr |= TEGRA_APBDMA_CSR_IE_EOC;
+	ch_regs->csr |= tdc->slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT;
+
+	ch_regs->apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1;
+
+	return 0;
+}
+
 static void tegra_dma_prep_wcount(struct tegra_dma_channel *tdc,
 	struct tegra_dma_channel_regs *ch_regs, u32 len)
 {
@@ -935,6 +1027,24 @@ static void tegra_dma_prep_wcount(struct tegra_dma_channel *tdc,
 		ch_regs->csr |= len_field;
 }
 
+static void tegra_dma_set_xfer_params(struct tegra_dma_channel *tdc,
+				      struct tegra_dma_sg_req *sg_req,
+				      struct tegra_dma_sg_req *sg_base,
+				      enum dma_transfer_direction direction,
+				      u32 mem, u32 len)
+{
+	sg_req->ch_regs.ahb_seq |= get_burst_size(tdc, direction, len);
+	sg_req->ch_regs.apb_ptr = sg_base->ch_regs.apb_ptr;
+	sg_req->ch_regs.ahb_ptr = mem;
+	sg_req->ch_regs.csr = sg_base->ch_regs.csr;
+	tegra_dma_prep_wcount(tdc, &sg_req->ch_regs, len);
+	sg_req->ch_regs.apb_seq = sg_base->ch_regs.apb_seq;
+	sg_req->ch_regs.ahb_seq = sg_base->ch_regs.ahb_seq;
+	sg_req->configured = false;
+	sg_req->last_sg = false;
+	sg_req->req_len = len;
+}
+
 static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
 	struct dma_chan *dc, struct scatterlist *sgl, unsigned int sg_len,
 	enum dma_transfer_direction direction, unsigned long flags,
@@ -942,13 +1052,10 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
 {
 	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
 	struct tegra_dma_desc *dma_desc;
-	unsigned int	    i;
-	struct scatterlist      *sg;
-	unsigned long csr, ahb_seq, apb_ptr, apb_seq;
+	unsigned int i;
+	struct scatterlist *sg;
 	struct list_head req_list;
-	struct tegra_dma_sg_req  *sg_req = NULL;
-	u32 burst_size;
-	enum dma_slave_buswidth slave_bw;
+	struct tegra_dma_sg_req sg_base, *sg_req = NULL;
 
 	if (!tdc->config_init) {
 		dev_err(tdc2dev(tdc), "dma channel is not configured\n");
@@ -959,24 +1066,11 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
 		return NULL;
 	}
 
-	if (get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
-				&burst_size, &slave_bw) < 0)
+	if (tegra_dma_get_xfer_params_sg(tdc, &sg_base, direction, flags) < 0)
 		return NULL;
 
 	INIT_LIST_HEAD(&req_list);
 
-	ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB;
-	ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE <<
-					TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
-	ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32;
-
-	csr |= TEGRA_APBDMA_CSR_ONCE | TEGRA_APBDMA_CSR_FLOW;
-	csr |= tdc->slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT;
-	if (flags & DMA_PREP_INTERRUPT)
-		csr |= TEGRA_APBDMA_CSR_IE_EOC;
-
-	apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1;
-
 	dma_desc = tegra_dma_desc_get(tdc);
 	if (!dma_desc) {
 		dev_err(tdc2dev(tdc), "Dma descriptors not available\n");
@@ -1011,19 +1105,11 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg(
 			return NULL;
 		}
 
-		ahb_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
 		dma_desc->bytes_requested += len;
 
-		sg_req->ch_regs.apb_ptr = apb_ptr;
-		sg_req->ch_regs.ahb_ptr = mem;
-		sg_req->ch_regs.csr = csr;
-		tegra_dma_prep_wcount(tdc, &sg_req->ch_regs, len);
-		sg_req->ch_regs.apb_seq = apb_seq;
-		sg_req->ch_regs.ahb_seq = ahb_seq;
-		sg_req->configured = false;
-		sg_req->last_sg = false;
+		tegra_dma_set_xfer_params(tdc, sg_req, &sg_base, direction,
+					  mem, len);
 		sg_req->dma_desc = dma_desc;
-		sg_req->req_len = len;
 
 		list_add_tail(&sg_req->node, &dma_desc->tx_list);
 	}
@@ -1056,13 +1142,10 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
 {
 	struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
 	struct tegra_dma_desc *dma_desc = NULL;
-	struct tegra_dma_sg_req  *sg_req = NULL;
-	unsigned long csr, ahb_seq, apb_ptr, apb_seq;
+	struct tegra_dma_sg_req sg_base, *sg_req = NULL;
 	int len;
 	size_t remain_len;
 	dma_addr_t mem = buf_addr;
-	u32 burst_size;
-	enum dma_slave_buswidth slave_bw;
 
 	if (!buf_len || !period_len) {
 		dev_err(tdc2dev(tdc), "Invalid buffer/period len\n");
@@ -1101,22 +1184,10 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
 		return NULL;
 	}
 
-	if (get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr,
-				&burst_size, &slave_bw) < 0)
+	if (tegra_dma_get_xfer_params_cyclic(tdc, &sg_base, direction,
+					     flags) < 0)
 		return NULL;
 
-	ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB;
-	ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE <<
-					TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT;
-	ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32;
-
-	csr |= TEGRA_APBDMA_CSR_FLOW;
-	if (flags & DMA_PREP_INTERRUPT)
-		csr |= TEGRA_APBDMA_CSR_IE_EOC;
-	csr |= tdc->slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT;
-
-	apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1;
-
 	dma_desc = tegra_dma_desc_get(tdc);
 	if (!dma_desc) {
 		dev_err(tdc2dev(tdc), "not enough descriptors available\n");
@@ -1140,17 +1211,9 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
 			return NULL;
 		}
 
-		ahb_seq |= get_burst_size(tdc, burst_size, slave_bw, len);
-		sg_req->ch_regs.apb_ptr = apb_ptr;
-		sg_req->ch_regs.ahb_ptr = mem;
-		sg_req->ch_regs.csr = csr;
-		tegra_dma_prep_wcount(tdc, &sg_req->ch_regs, len);
-		sg_req->ch_regs.apb_seq = apb_seq;
-		sg_req->ch_regs.ahb_seq = ahb_seq;
-		sg_req->configured = false;
-		sg_req->last_sg = false;
+		tegra_dma_set_xfer_params(tdc, sg_req, &sg_base, direction,
+					  mem, len);
 		sg_req->dma_desc = dma_desc;
-		sg_req->req_len = len;
 
 		list_add_tail(&sg_req->node, &dma_desc->tx_list);
 		remain_len -= len;
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[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