From 1b140908c4cda43c653bb080c244d112e619008f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:02 +0530 Subject: dma: tegra: add support for channel wise pause NVIDIA's some SoCs like Tegra114 support the channel wise pause control inplace of global pause which pauses all DMA channels. When SoCs support the channel wise pause control then it uses the global pause for clock gating for register access as well as all DMA channel pause. Hence DMA registers are not accessible if DMAs are globally paused on these new SoCs. Add support for channel wise pause feature if SoCs support it. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'drivers/dma/tegra20-apb-dma.c') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index efdfffa13349..2c46ac46e7ad 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -62,6 +62,9 @@ #define TEGRA_APBDMA_STATUS_COUNT_SHIFT 2 #define TEGRA_APBDMA_STATUS_COUNT_MASK 0xFFFC +#define TEGRA_APBDMA_CHAN_CSRE 0x00C +#define TEGRA_APBDMA_CHAN_CSRE_PAUSE (1 << 31) + /* AHB memory address */ #define TEGRA_APBDMA_CHAN_AHBPTR 0x010 @@ -112,10 +115,12 @@ struct tegra_dma; * tegra_dma_chip_data Tegra chip specific DMA data * @nr_channels: Number of channels available in the controller. * @max_dma_count: Maximum DMA transfer count supported by DMA controller. + * @support_channel_pause: Support channel wise pause of dma. */ struct tegra_dma_chip_data { int nr_channels; int max_dma_count; + bool support_channel_pause; }; /* DMA channel registers */ @@ -353,6 +358,32 @@ static void tegra_dma_global_resume(struct tegra_dma_channel *tdc) spin_unlock(&tdma->global_lock); } +static void tegra_dma_pause(struct tegra_dma_channel *tdc, + bool wait_for_burst_complete) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, + TEGRA_APBDMA_CHAN_CSRE_PAUSE); + if (wait_for_burst_complete) + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); + } else { + tegra_dma_global_pause(tdc, wait_for_burst_complete); + } +} + +static void tegra_dma_resume(struct tegra_dma_channel *tdc) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, 0); + } else { + tegra_dma_global_resume(tdc); + } +} + static void tegra_dma_stop(struct tegra_dma_channel *tdc) { u32 csr; @@ -408,7 +439,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, * If there is already IEC status then interrupt handler need to * load new configuration. */ - tegra_dma_global_pause(tdc, false); + tegra_dma_pause(tdc, false); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); /* @@ -418,7 +449,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { dev_err(tdc2dev(tdc), "Skipping new configuration as interrupt is pending\n"); - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); return; } @@ -429,7 +460,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB); nsg_req->configured = true; - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); } static void tdc_start_head_req(struct tegra_dma_channel *tdc) @@ -690,7 +721,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) goto skip_dma_stop; /* Pause DMA before checking the queue status */ - tegra_dma_global_pause(tdc, true); + tegra_dma_pause(tdc, true); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { @@ -708,7 +739,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) sgreq->dma_desc->bytes_transferred += get_current_xferred_count(tdc, sgreq, status); } - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); skip_dma_stop: tegra_dma_abort_all(tdc); @@ -1175,6 +1206,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc) static const struct tegra_dma_chip_data tegra20_dma_chip_data = { .nr_channels = 16, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; #if defined(CONFIG_OF) @@ -1182,6 +1214,7 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = { static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .nr_channels = 32, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; static const struct of_device_id tegra_dma_of_match[] __devinitconst = { -- cgit From 5ea7caf30debefc1c4319f77146288fd5e92a803 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:03 +0530 Subject: dma: tegra: add support for Tegra114 SoC NVIDIA's Tegra114 has APB DMA controller which has 32 dma channels and support support channel wise pause control. Add support for Tegra114 which uses the channel wise pause control hardware feature. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/dma/tegra20-apb-dma.c') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 2c46ac46e7ad..6c144814a896 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1217,8 +1217,19 @@ static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .support_channel_pause = false, }; -static const struct of_device_id tegra_dma_of_match[] __devinitconst = { +/* Tegra114 specific DMA controller information */ +static const struct tegra_dma_chip_data tegra114_dma_chip_data = { + .nr_channels = 32, + .max_dma_count = 1024UL * 64, + .support_channel_pause = true, +}; + + +static const struct of_device_id tegra_dma_of_match[] = { { + .compatible = "nvidia,tegra114-apbdma", + .data = &tegra114_dma_chip_data, + }, { .compatible = "nvidia,tegra30-apbdma", .data = &tegra30_dma_chip_data, }, { -- cgit From a72208733f7866972bcba7995f7e598ad07f1158 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:20 +0200 Subject: dma: tegra20-apb-dma: remove unnecessary assignment There is no need to assign 0 to residue, because dma_cookie_status() does this for us. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/dma/tegra20-apb-dma.c') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 6c144814a896..c71812f8d9c1 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -767,7 +767,6 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, ret = dma_cookie_status(dc, cookie, txstate); if (ret == DMA_SUCCESS) { - dma_set_residue(txstate, 0); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } -- cgit