diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-09 21:00:07 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-09 21:00:07 -0700 |
commit | dcded10f6dce10411b16134ce9cc87bfdf75c13f (patch) | |
tree | da6d0face147072c9eadc8a01af4e49958769138 | |
parent | 9e0ba741aabdf129d561e62d207c75146adef5e8 (diff) | |
parent | b0ebeb9c09cb5b84bb2f33927c84c7648845fcec (diff) |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx: (30 commits)
DMAENGINE: at_hdmac: locking fixlet
DMAENGINE: pch_dma: kill another usage of __raw_{read|write}l
dma: dmatest: fix potential sign bug
ioat2: catch and recover from broken vtd configurations v6
DMAENGINE: add runtime slave control to COH 901 318 v3
DMAENGINE: add runtime slave config to DMA40 v3
DMAENGINE: generic slave channel control v3
dmaengine: Driver for Topcliff PCH DMA controller
intel_mid: Add Mrst & Mfld DMA Drivers
drivers/dma: Eliminate a NULL pointer dereference
dma/timb_dma: compile warning on 32 bit
DMAENGINE: ste_dma40: support older silicon
DMAENGINE: ste_dma40: support disabling physical channels
DMAENGINE: ste_dma40: no disabled phy channels on ux500
DMAENGINE: ste_dma40: fix suspend bug
DMAENGINE: ste_dma40: add DB8500 memcpy channels
DMAENGINE: ste_dma40: no flow control on memcpy
DMAENGINE: ste_dma40: arch updates for LCLA and LCPA
DMAENGINE: ste_dma40: allocate LCLA dynamically
DMAENGINE: ste_dma40: no premature stop
...
Fix up trivial conflicts in arch/arm/mach-ux500/devices-db8500.c
-rw-r--r-- | arch/arm/mach-ux500/devices-db8500.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-ux500/include/mach/db8500-regs.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-ux500/ste-dma40-db8500.h | 2 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/ste_dma40.h | 4 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 20 | ||||
-rw-r--r-- | drivers/dma/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/at_hdmac.c | 4 | ||||
-rw-r--r-- | drivers/dma/coh901318.c | 169 | ||||
-rw-r--r-- | drivers/dma/dmatest.c | 2 | ||||
-rw-r--r-- | drivers/dma/intel_mid_dma.c | 1143 | ||||
-rw-r--r-- | drivers/dma/intel_mid_dma_regs.h | 260 | ||||
-rw-r--r-- | drivers/dma/ioat/dma.h | 1 | ||||
-rw-r--r-- | drivers/dma/ioat/dma_v2.c | 24 | ||||
-rw-r--r-- | drivers/dma/ioat/dma_v3.c | 5 | ||||
-rw-r--r-- | drivers/dma/pch_dma.c | 957 | ||||
-rw-r--r-- | drivers/dma/ste_dma40.c | 860 | ||||
-rw-r--r-- | drivers/dma/ste_dma40_ll.c | 40 | ||||
-rw-r--r-- | drivers/dma/ste_dma40_ll.h | 15 | ||||
-rw-r--r-- | drivers/dma/timb_dma.c | 8 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 28 | ||||
-rw-r--r-- | include/linux/dmaengine.h | 71 | ||||
-rw-r--r-- | include/linux/intel_mid_dma.h | 86 | ||||
-rw-r--r-- | include/linux/pch_dma.h | 37 |
23 files changed, 3390 insertions, 382 deletions
diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c index 654fca944e6..9280d256111 100644 --- a/arch/arm/mach-ux500/devices-db8500.c +++ b/arch/arm/mach-ux500/devices-db8500.c @@ -113,26 +113,21 @@ struct platform_device u8500_i2c4_device = { static struct resource dma40_resources[] = { [0] = { .start = U8500_DMA_BASE, - .end = U8500_DMA_BASE + SZ_4K - 1, + .end = U8500_DMA_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, - .name = "base", + .name = "base", }, [1] = { .start = U8500_DMA_LCPA_BASE, - .end = U8500_DMA_LCPA_BASE + SZ_4K - 1, + .end = U8500_DMA_LCPA_BASE + 2 * SZ_1K - 1, .flags = IORESOURCE_MEM, - .name = "lcpa", + .name = "lcpa", }, [2] = { - .start = U8500_DMA_LCLA_BASE, - .end = U8500_DMA_LCLA_BASE + 16 * 1024 - 1, - .flags = IORESOURCE_MEM, - .name = "lcla", - }, - [3] = { .start = IRQ_DB8500_DMA, .end = IRQ_DB8500_DMA, - .flags = IORESOURCE_IRQ} + .flags = IORESOURCE_IRQ, + } }; /* Default configuration for physcial memcpy */ @@ -145,11 +140,12 @@ struct stedma40_chan_cfg dma40_memcpy_conf_phy = { .src_info.endianess = STEDMA40_LITTLE_ENDIAN, .src_info.data_width = STEDMA40_BYTE_WIDTH, .src_info.psize = STEDMA40_PSIZE_PHY_1, + .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, .dst_info.endianess = STEDMA40_LITTLE_ENDIAN, .dst_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.psize = STEDMA40_PSIZE_PHY_1, - + .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, }; /* Default configuration for logical memcpy */ struct stedma40_chan_cfg dma40_memcpy_conf_log = { @@ -162,11 +158,12 @@ struct stedma40_chan_cfg dma40_memcpy_conf_log = { .src_info.endianess = STEDMA40_LITTLE_ENDIAN, .src_info.data_width = STEDMA40_BYTE_WIDTH, .src_info.psize = STEDMA40_PSIZE_LOG_1, + .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, .dst_info.endianess = STEDMA40_LITTLE_ENDIAN, .dst_info.data_width = STEDMA40_BYTE_WIDTH, .dst_info.psize = STEDMA40_PSIZE_LOG_1, - + .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL, }; /* @@ -180,10 +177,12 @@ static const dma_addr_t dma40_rx_map[STEDMA40_NR_DEV]; /* Reserved event lines for memcpy only */ static int dma40_memcpy_event[] = { + STEDMA40_MEMCPY_TX_0, STEDMA40_MEMCPY_TX_1, STEDMA40_MEMCPY_TX_2, STEDMA40_MEMCPY_TX_3, STEDMA40_MEMCPY_TX_4, + STEDMA40_MEMCPY_TX_5, }; static struct stedma40_platform_data dma40_plat_data = { @@ -195,6 +194,7 @@ static struct stedma40_platform_data dma40_plat_data = { .memcpy_conf_phy = &dma40_memcpy_conf_phy, .memcpy_conf_log = &dma40_memcpy_conf_log, .llis_per_log = 8, + .disabled_channels = {-1}, }; struct platform_device u8500_dma40_device = { @@ -213,4 +213,6 @@ void dma40_u8500ed_fixup(void) dma40_plat_data.memcpy_len = 0; dma40_resources[0].start = U8500_DMA_BASE_ED; dma40_resources[0].end = U8500_DMA_BASE_ED + SZ_4K - 1; + dma40_resources[1].start = U8500_DMA_LCPA_BASE_ED; + dma40_resources[1].end = U8500_DMA_LCPA_BASE_ED + 2 * SZ_1K - 1; } diff --git a/arch/arm/mach-ux500/include/mach/db8500-regs.h b/arch/arm/mach-ux500/include/mach/db8500-regs.h index 85fc6a80b38..f000218210c 100644 --- a/arch/arm/mach-ux500/include/mach/db8500-regs.h +++ b/arch/arm/mach-ux500/include/mach/db8500-regs.h @@ -15,9 +15,9 @@ #define U8500_ESRAM_BANK2 (U8500_ESRAM_BANK1 + U8500_ESRAM_BANK_SIZE) #define U8500_ESRAM_BANK3 (U8500_ESRAM_BANK2 + U8500_ESRAM_BANK_SIZE) #define U8500_ESRAM_BANK4 (U8500_ESRAM_BANK3 + U8500_ESRAM_BANK_SIZE) -/* Use bank 4 for DMA LCLA and LCPA */ -#define U8500_DMA_LCLA_BASE U8500_ESRAM_BANK4 -#define U8500_DMA_LCPA_BASE (U8500_ESRAM_BANK4 + 0x4000) +/* Use bank 4 for DMA LCPA */ +#define U8500_DMA_LCPA_BASE U8500_ESRAM_BANK4 +#define U8500_DMA_LCPA_BASE_ED (U8500_ESRAM_BANK4 + 0x4000) #define U8500_PER3_BASE 0x80000000 #define U8500_STM_BASE 0x80100000 diff --git a/arch/arm/mach-ux500/ste-dma40-db8500.h b/arch/arm/mach-ux500/ste-dma40-db8500.h index e7016278dfa..9d9d3797b3b 100644 --- a/arch/arm/mach-ux500/ste-dma40-db8500.h +++ b/arch/arm/mach-ux500/ste-dma40-db8500.h @@ -136,7 +136,7 @@ enum dma_dest_dev_type { STEDMA40_DEV_CAC1_TX = 48, STEDMA40_DEV_CAC1_TX_HAC1_TX = 49, STEDMA40_DEV_HAC1_TX = 50, - STEDMA40_MEMXCPY_TX_0 = 51, + STEDMA40_MEMCPY_TX_0 = 51, STEDMA40_DEV_SLIM1_CH0_TX_HSI_TX_CH4 = 52, STEDMA40_DEV_SLIM1_CH1_TX_HSI_TX_CH5 = 53, STEDMA40_DEV_SLIM1_CH2_TX_HSI_TX_CH6 = 54, diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h index 4d12ea4ca36..5fbde4b8dc1 100644 --- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h +++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h @@ -148,7 +148,8 @@ struct stedma40_chan_cfg { * @memcpy_conf_phy: default configuration of physical channel memcpy * @memcpy_conf_log: default configuration of logical channel memcpy * @llis_per_log: number of max linked list items per logical channel - * + * @disabled_channels: A vector, ending with -1, that marks physical channels + * that are for different reasons not available for the driver. */ struct stedma40_platform_data { u32 dev_len; @@ -159,6 +160,7 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; unsigned int llis_per_log; + int disabled_channels[8]; }; /** diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 9e01e96fee9..fed57634b6c 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -33,6 +33,19 @@ if DMADEVICES comment "DMA Devices" +config INTEL_MID_DMAC + tristate "Intel MID DMA support for Peripheral DMA controllers" + depends on PCI && X86 + select DMA_ENGINE + default n + help + Enable support for the Intel(R) MID DMA engine present + in Intel MID chipsets. + + Say Y here if you have such a chipset. + + If unsure, say N. + config ASYNC_TX_DISABLE_CHANNEL_SWITCH bool @@ -175,6 +188,13 @@ config PL330_DMA You need to provide platform specific settings via platform_data for a dma-pl330 device. +config PCH_DMA + tristate "Topcliff PCH DMA support" + depends on PCI && X86 + select DMA_ENGINE + help + Enable support for the Topcliff PCH DMA engine. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 0fe5ebbfda5..72bd70384d8 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -7,6 +7,7 @@ endif obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_NET_DMA) += iovlock.o +obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o @@ -23,3 +24,4 @@ obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ obj-$(CONFIG_TIMB_DMA) += timb_dma.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o obj-$(CONFIG_PL330_DMA) += pl330.o +obj-$(CONFIG_PCH_DMA) += pch_dma.o diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index e88076022a7..a0f3e6a06e0 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -790,12 +790,12 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, list_splice_init(&atchan->queue, &list); list_splice_init(&atchan->active_list, &list); - spin_unlock_bh(&atchan->lock); - /* Flush all pending and queued descriptors */ list_for_each_entry_safe(desc, _desc, &list, desc_node) atc_chain_complete(atchan, desc); + spin_unlock_bh(&atchan->lock); + return 0; } diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index a724e6be1b4..557e2272e5b 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -72,6 +72,9 @@ struct coh901318_chan { unsigned long nbr_active_done; unsigned long busy; + u32 runtime_addr; + u32 runtime_ctrl; + struct coh901318_base *base; }; @@ -190,6 +193,9 @@ static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan) static inline dma_addr_t cohc_dev_addr(struct coh901318_chan *cohc) { + /* Runtime supplied address will take precedence */ + if (cohc->runtime_addr) + return cohc->runtime_addr; return cohc->base->platform->chan_conf[cohc->id].dev_addr; } @@ -1055,6 +1061,14 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, params = cohc_chan_param(cohc); config = params->config; + /* + * Add runtime-specific control on top, make + * sure the bits you set per peripheral channel are + * cleared in the default config from the platform. + */ + ctrl_chained |= cohc->runtime_ctrl; + ctrl_last |= cohc->runtime_ctrl; + ctrl |= cohc->runtime_ctrl; if (direction == DMA_TO_DEVICE) { u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | @@ -1113,6 +1127,12 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (ret) goto err_lli_fill; + /* + * Set the default ctrl for the channel to the one from the lli, + * things may have changed due to odd buffer alignment etc. + */ + coh901318_set_ctrl(cohc, lli->control); + COH_DBG(coh901318_list_print(cohc, lli)); /* Pick a descriptor to handle this transfer */ @@ -1175,6 +1195,146 @@ coh901318_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&cohc->lock, flags); } +/* + * Here we wrap in the runtime dma control interface + */ +struct burst_table { + int burst_8bit; + int burst_16bit; + int burst_32bit; + u32 reg; +}; + +static const struct burst_table burst_sizes[] = { + { + .burst_8bit = 64, + .burst_16bit = 32, + .burst_32bit = 16, + .reg = COH901318_CX_CTRL_BURST_COUNT_64_BYTES, + }, + { + .burst_8bit = 48, + .burst_16bit = 24, + .burst_32bit = 12, + .reg = COH901318_CX_CTRL_BURST_COUNT_48_BYTES, + }, + { + .burst_8bit = 32, + .burst_16bit = 16, + .burst_32bit = 8, + .reg = COH901318_CX_CTRL_BURST_COUNT_32_BYTES, + }, + { + .burst_8bit = 16, + .burst_16bit = 8, + .burst_32bit = 4, + .reg = COH901318_CX_CTRL_BURST_COUNT_16_BYTES, + }, + { + .burst_8bit = 8, + .burst_16bit = 4, + .burst_32bit = 2, + .reg = COH901318_CX_CTRL_BURST_COUNT_8_BYTES, + }, + { + .burst_8bit = 4, + .burst_16bit = 2, + .burst_32bit = 1, + .reg = COH901318_CX_CTRL_BURST_COUNT_4_BYTES, + }, + { + .burst_8bit = 2, + .burst_16bit = 1, + .burst_32bit = 0, + .reg = COH901318_CX_CTRL_BURST_COUNT_2_BYTES, + }, + { + .burst_8bit = 1, + .burst_16bit = 0, + .burst_32bit = 0, + .reg = COH901318_CX_CTRL_BURST_COUNT_1_BYTE, + }, +}; + +static void coh901318_dma_set_runtimeconfig(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct coh901318_chan *cohc = to_coh901318_chan(chan); + dma_addr_t addr; + enum dma_slave_buswidth addr_width; + u32 maxburst; + u32 runtime_ctrl = 0; + int i = 0; + + /* We only support mem to per or per to mem transfers */ + if (config->direction == DMA_FROM_DEVICE) { + addr = config->src_addr; + addr_width = config->src_addr_width; + maxburst = config->src_maxburst; + } else if (config->direction == DMA_TO_DEVICE) { + addr = config->dst_addr; + addr_width = config->dst_addr_width; + maxburst = config->dst_maxburst; + } else { + dev_err(COHC_2_DEV(cohc), "illegal channel mode\n"); + return; + } + + dev_dbg(COHC_2_DEV(cohc), "configure channel for %d byte transfers\n", + addr_width); + switch (addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + runtime_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_8_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_8_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_8bit <= maxburst) + break; + i++; + } + + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + runtime_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_16_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_16_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_16bit <= maxburst) + break; + i++; + } + + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + /* Direction doesn't matter here, it's 32/32 bits */ + runtime_ctrl |= + COH901318_CX_CTRL_SRC_BUS_SIZE_32_BITS | + COH901318_CX_CTRL_DST_BUS_SIZE_32_BITS; + + while (i < ARRAY_SIZE(burst_sizes)) { + if (burst_sizes[i].burst_32bit <= maxburst) + break; + i++; + } + + break; + default: + dev_err(COHC_2_DEV(cohc), + "bad runtimeconfig: alien address width\n"); + return; + } + + runtime_ctrl |= burst_sizes[i].reg; + dev_dbg(COHC_2_DEV(cohc), + "selected burst size %d bytes for address width %d bytes, maxburst %d\n", + burst_sizes[i].burst_8bit, addr_width, maxburst); + + cohc->runtime_addr = addr; + cohc->runtime_ctrl = runtime_ctrl; +} + static int coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) @@ -1184,6 +1344,14 @@ coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct coh901318_desc *cohd; void __iomem *virtbase = cohc->base->virtbase; + if (cmd == DMA_SLAVE_CONFIG) { + struct dma_slave_config *config = + (struct dma_slave_config *) arg; + + coh901318_dma_set_runtimeconfig(chan, config); + return 0; + } + if (cmd == DMA_PAUSE) { coh901318_pause(chan); return 0; @@ -1240,6 +1408,7 @@ coh901318_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, return 0; } + void coh901318_base_init(struct dma_device *dma, const int *pick_chans, struct coh901318_base *base) { diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 68d58c414cf..5589358b684 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -540,7 +540,7 @@ static int dmatest_add_channel(struct dma_chan *chan) struct dmatest_chan *dtc; struct dma_device *dma_dev = chan->device; unsigned int thread_count = 0; - unsigned int cnt; + int cnt; dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); if (!dtc) { diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c new file mode 100644 index 00000000000..c2591e8d9b6 --- /dev/null +++ b/drivers/dma/intel_mid_dma.c @@ -0,0 +1,1143 @@ +/* + * intel_mid_dma.c - Intel Langwell DMA Drivers + * + * Copyright (C) 2008-10 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * The driver design is based on dw_dmac driver + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/intel_mid_dma.h> + +#define MAX_CHAN 4 /*max ch across controllers*/ +#include "intel_mid_dma_regs.h" + +#define INTEL_MID_DMAC1_ID 0x0814 +#define INTEL_MID_DMAC2_ID 0x0813 +#define INTEL_MID_GP_DMAC2_ID 0x0827 +#define INTEL_MFLD_DMAC1_ID 0x0830 +#define LNW_PERIPHRAL_MASK_BASE 0xFFAE8008 +#define LNW_PERIPHRAL_MASK_SIZE 0x10 +#define LNW_PERIPHRAL_STATUS 0x0 +#define LNW_PERIPHRAL_MASK 0x8 + +struct intel_mid_dma_probe_info { + u8 max_chan; + u8 ch_base; + u16 block_size; + u32 pimr_mask; +}; + +#define INFO(_max_chan, _ch_base, _block_size, _pimr_mask) \ + ((kernel_ulong_t)&(struct intel_mid_dma_probe_info) { \ + .max_chan = (_max_chan), \ + .ch_base = (_ch_base), \ + .block_size = (_block_size), \ + .pimr_mask = (_pimr_mask), \ + }) + +/***************************************************************************** +Utility Functions*/ +/** + * get_ch_index - convert status to channel + * @status: status mask + * @base: dma ch base value + * + * Modify the status mask and return the channel index needing + * attention (or -1 if neither) + */ +static int get_ch_index(int *status, unsigned int base) +{ + int i; + for (i = 0; i < MAX_CHAN; i++) { + if (*status & (1 << (i + base))) { + *status = *status & ~(1 << (i + base)); + pr_debug("MDMA: index %d New status %x\n", i, *status); + return i; + } + } + return -1; +} + +/** + * get_block_ts - calculates dma transaction length + * @len: dma transfer length + * @tx_width: dma transfer src width + * @block_size: dma controller max block size + * + * Based on src width calculate the DMA trsaction length in data items + * return data items or FFFF if exceeds max length for block + */ +static int get_block_ts(int len, int tx_width, int block_size) +{ + int byte_width = 0, block_ts = 0; + + switch (tx_width) { + case LNW_DMA_WIDTH_8BIT: + byte_width = 1; + break; + case LNW_DMA_WIDTH_16BIT: + byte_width = 2; + break; + case LNW_DMA_WIDTH_32BIT: + default: + byte_width = 4; + break; + } + + block_ts = len/byte_width; + if (block_ts > block_size) + block_ts = 0xFFFF; + return block_ts; +} + +/***************************************************************************** +DMAC1 interrupt Functions*/ + +/** + * dmac1_mask_periphral_intr - mask the periphral interrupt + * @midc: dma channel for which masking is required + * + * Masks the DMA periphral interrupt + * this is valid for DMAC1 family controllers only + * This controller should have periphral mask registers already mapped + */ +static void dmac1_mask_periphral_intr(struct intel_mid_dma_chan *midc) +{ + u32 pimr; + struct middma_device *mid = to_middma_device(midc->chan.device); + + if (mid->pimr_mask) { + pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK); + pimr |= mid->pimr_mask; + writel(pimr, mid->mask_reg + LNW_PERIPHRAL_MASK); + } + return; +} + +/** + * dmac1_unmask_periphral_intr - unmask the periphral interrupt + * @midc: dma channel for which masking is required + * + * UnMasks the DMA periphral interrupt, + * this is valid for DMAC1 family controllers only + * This controller should have periphral mask registers already mapped + */ +static void dmac1_unmask_periphral_intr(struct intel_mid_dma_chan *midc) +{ + u32 pimr; + struct middma_device *mid = to_middma_device(midc->chan.device); + + if (mid->pimr_mask) { + pimr = readl(mid->mask_reg + LNW_PERIPHRAL_MASK); + pimr &= ~mid->pimr_mask; + writel(pimr, mid->mask_reg + LNW_PERIPHRAL_MASK); + } + return; +} + +/** + * enable_dma_interrupt - enable the periphral interrupt + * @midc: dma channel for which enable interrupt is required + * + * Enable the DMA periphral interrupt, + * this is valid for DMAC1 family controllers only + * This controller should have periphral mask registers already mapped + */ +static void enable_dma_interrupt(struct intel_mid_dma_chan *midc) +{ + dmac1_unmask_periphral_intr(midc); + + /*en ch interrupts*/ + iowrite32(UNMASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR); + iowrite32(UNMASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR); + return; +} + +/** + * disable_dma_interrupt - disable the periphral interrupt + * @midc: dma channel for which disable interrupt is required + * + * Disable the DMA periphral interrupt, + * this is valid for DMAC1 family controllers only + * This controller should have periphral mask registers already mapped + */ +static void disable_dma_interrupt(struct intel_mid_dma_chan *midc) +{ + /*Check LPE PISR, make sure fwd is disabled*/ + dmac1_mask_periphral_intr(midc); + iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_BLOCK); + iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_TFR); + iowrite32(MASK_INTR_REG(midc->ch_id), midc->dma_base + MASK_ERR); + return; +} + +/***************************************************************************** +DMA channel helper Functions*/ +/** + * mid_desc_get - get a descriptor + * @midc: dma channel for which descriptor is required + * + * Obtain a descriptor for the channel. Returns NULL if none are free. + * Once the descriptor is returned it is private until put on another + * list or freed + */ +static struct intel_mid_dma_desc *midc_desc_get(struct intel_mid_dma_chan *midc) +{ + struct intel_mid_dma_desc *desc, *_desc; + struct intel_mid_dma_desc *ret = NULL; + + spin_lock_bh(&midc->lock); + list_for_each_entry_safe(desc, _desc, &midc->free_list, desc_node) { + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + } + spin_unlock_bh(&midc->lock); + return ret; +} + +/** + * mid_desc_put - put a descriptor + * @midc: dma channel for which descriptor is required + * @desc: descriptor to put + * + * Return a descriptor from lwn_desc_get back to the free pool + */ +static void midc_desc_put(struct intel_mid_dma_chan *midc, + struct intel_mid_dma_desc *desc) +{ + if (desc) { + spin_lock_bh(&midc->lock); + list_add_tail(&desc->desc_node, &midc->free_list); + spin_unlock_bh(&midc->lock); + } +} +/** + * midc_dostart - begin a DMA transaction + * @midc: channel for which txn is to be started + * @first: first descriptor of series + * + * Load a transaction into the engine. This must be called with midc->lock + * held and bh disabled. + */ +static void midc_dostart(struct intel_mid_dma_chan *midc, + struct intel_mid_dma_desc *first) +{ + struct middma_device *mid = to_middma_device(midc->chan.device); + + /* channel is idle */ + if (midc->in_use && test_ch_en(midc->dma_base, midc->ch_id)) { + /*error*/ + pr_err("ERR_MDMA: channel is busy in start\n"); + /* The tasklet will hopefully advance the queue... */ + return; + } + + /*write registers and en*/ + iowrite32(first->sar, midc->ch_regs + SAR); + iowrite32(first->dar, midc->ch_regs + DAR); + iowrite32(first->cfg_hi, midc->ch_regs + CFG_HIGH); + iowrite32(first->cfg_lo, midc->ch_regs + CFG_LOW); + iowrite32(first->ctl_lo, midc->ch_regs + CTL_LOW); + iowrite32(first->ctl_hi, midc->ch_regs + CTL_HIGH); + pr_debug("MDMA:TX SAR %x,DAR %x,CFGL %x,CFGH %x,CTLH %x, CTLL %x\n", + (int)first->sar, (int)first->dar, first->cfg_hi, + first->cfg_lo, first->ctl_hi, first->ctl_lo); + + iowrite32(ENABLE_CHANNEL(midc->ch_id), mid->dma_base + DMA_CHAN_EN); + first->status = DMA_IN_PROGRESS; +} + +/** + * midc_descriptor_complete - process completed descriptor + * @midc: channel owning the descriptor + * @desc: the descriptor itself + * + * Process a completed descriptor and perform any callbacks upon + * the completion. The completion handling drops the lock during the + * callbacks but must be called with the lock held. + */ +static void midc_descriptor_complete(struct intel_mid_dma_chan *midc, + struct intel_mid_dma_desc *desc) +{ + struct dma_async_tx_descriptor *txd = &desc->txd; + dma_async_tx_callback callback_txd = NULL; + void *param_txd = NULL; + + midc->completed = txd->cookie; + callback_txd = txd->callback; + param_txd = txd->callback_param; + + list_move(&desc->desc_node, &midc->free_list); + + spin_unlock_bh(&midc->lock); + if (callback_txd) { + pr_debug("MDMA: TXD callback set ... calling\n"); + callback_txd(param_txd); + spin_lock_bh(&midc->lock); + return; + } + spin_lock_bh(&midc->lock); + +} +/** + * midc_scan_descriptors - check the descriptors in channel + * mark completed when tx is completete + * @mid: device + * @midc: channel to scan + * + * Walk the descriptor chain for the device and process any entries + * that are complete. + */ +static void midc_scan_descriptors(struct middma_device *mid, + struct intel_mid_dma_chan *midc) +{ + struct intel_mid_dma_desc *desc = NULL, *_desc = NULL; + + /*tx is complete*/ + list_for_each_entry_safe(desc, _desc, &midc->active_list, desc_node) { + if (desc->status == DMA_IN_PROGRESS) { + desc->status = DMA_SUCCESS; + midc_descriptor_complete(midc, desc); + } + } + return; +} + +/***************************************************************************** +DMA engine callback Functions*/ +/** + * intel_mid_dma_tx_submit - callback to submit DMA transaction + * @tx: dma engine descriptor + * + * Submit the DMA trasaction for this descriptor, start if ch idle + */ +static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct intel_mid_dma_desc *desc = to_intel_mid_dma_desc(tx); + struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(tx->chan); + dma_cookie_t cookie; + + spin_lock_bh(&midc->lock); + cookie = midc->chan.cookie; + + if (++cookie < 0) + cookie = 1; + + midc->chan.cookie = cookie; + desc->txd.cookie = cookie; + + + if (list_empty(&midc->active_list)) { + midc_dostart(midc, desc); + list_add_tail(&desc->desc_node, &midc->active_list); + } else { + list_add_tail(&desc->desc_node, &midc->queue); + } + spin_unlock_bh(&midc->lock); + + return cookie; +} + +/** + * intel_mid_dma_issue_pending - callback to issue pending txn + * @chan: chan where pending trascation needs to be checked and submitted + * + * Call for scan to issue pending descriptors + */ +static void intel_mid_dma_issue_pending(struct dma_chan *chan) +{ + struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); + + spin_lock_bh(&midc->lock); + if (!list_empty(&midc->queue)) + midc_scan_descriptors(to_middma_device(chan->device), midc); + spin_unlock_bh(&midc->lock); +} + +/** + * intel_mid_dma_tx_status - Return status of txn + * @chan: chan for where status needs to be checked + * @cookie: cookie for txn + * @txstate: DMA txn state + * + * Return status of DMA txn + */ +static enum dma_status intel_mid_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); + dma_cookie_t last_used; + dma_cookie_t last_complete; + int ret; + + last_complete = midc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + if (ret != DMA_SUCCESS) { + midc_scan_descriptors(to_middma_device(chan->device), midc); + + last_complete = midc->completed; + last_used = chan->cookie; + + ret = dma_async_is_complete(cookie, last_complete, last_used); + } + + if (txstate) { + txstate->last = last_complete; + txstate->used = last_used; + txstate->residue = 0; + } + return ret; +} + +/** + * intel_mid_dma_device_control - DMA device control + * @chan: chan for DMA control + * @cmd: control cmd + * @arg: cmd arg value + * + * Perform DMA control command + */ +static int intel_mid_dma_device_control(struct dma_chan *chan, + enum dma_ctrl_cmd cmd, unsigned long arg) +{ + struct intel_mid_dma_chan *midc = to_intel_mid_dma_chan(chan); + struct middma_device *mid = to_middma_device(chan->device); + struct intel_mid_dma_desc *desc, *_desc; + LIST_HEAD(list); + + if (cmd != DMA_TERMINATE_ALL) + return -ENXIO; + + spin_lock_bh(&midc->lock); + if (midc->in_use == false) { + spin_unlock_bh(&midc->lock); + return 0; + } |