From d7cabeed830b7eb3958cbc084a85649126cd670f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Jun 2013 20:38:28 +0100 Subject: dmaengine: PL08x: Avoid collisions with get_signal() macro As pointed out by Arnd Bergmann there is a get_signal macro definied in linux/signal.h which can conflict with the platform data callback function of the same name leading to confusing errors from the compiler (especially if signal.h manages to get pulled into the driver itself due to header dependencies). Avoid such errors by renaming get_signal and put_signal in the platform data to get_xfer_signal and put_xfer_signal. Signed-off-by: Mark Brown Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- include/linux/amba/pl08x.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux/amba') diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 2a5f64a11b77..10fe2a211c2e 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -76,11 +76,11 @@ struct pl08x_channel_data { * platform, all inclusive, including multiplexed channels. The available * physical channels will be multiplexed around these signals as they are * requested, just enumerate all possible channels. - * @get_signal: request a physical signal to be used for a DMA transfer + * @get_xfer_signal: request a physical signal to be used for a DMA transfer * immediately: if there is some multiplexing or similar blocking the use * of the channel the transfer can be denied by returning less than zero, * else it returns the allocated signal number - * @put_signal: indicate to the platform that this physical signal is not + * @put_xfer_signal: indicate to the platform that this physical signal is not * running any DMA transfer and multiplexing can be recycled * @lli_buses: buses which LLIs can be fetched from: PL08X_AHB1 | PL08X_AHB2 * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2 @@ -89,8 +89,8 @@ struct pl08x_platform_data { const struct pl08x_channel_data *slave_channels; unsigned int num_slave_channels; struct pl08x_channel_data memcpy_channel; - int (*get_signal)(const struct pl08x_channel_data *); - void (*put_signal)(const struct pl08x_channel_data *, int); + int (*get_xfer_signal)(const struct pl08x_channel_data *); + void (*put_xfer_signal)(const struct pl08x_channel_data *, int); u8 lli_buses; u8 mem_buses; }; -- cgit v1.2.3 From da1b6c05b8b5bd9af060acb319360604a0890c2e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sun, 11 Aug 2013 19:59:17 +0200 Subject: dmaengine: PL08x: Add support for PL080S variant PL080S is a modified version of PL080 that can be found on Samsung SoCs, such as S3C6400 and S3C6410. It has different offset of CONFIG register, separate CONTROL1 register that holds transfer size and larger maximum transfer size. Signed-off-by: Tomasz Figa Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 145 ++++++++++++++++++++++++++++++++++++--------- include/linux/amba/pl080.h | 1 + 2 files changed, 119 insertions(+), 27 deletions(-) (limited to 'include/linux/amba') diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 1fa05d618141..75915be252f5 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -24,6 +24,7 @@ * * Documentation: ARM DDI 0196G == PL080 * Documentation: ARM DDI 0218E == PL081 + * Documentation: S3C6410 User's Manual == PL080S * * PL080 & PL081 both have 16 sets of DMA signals that can be routed to any * channel. @@ -36,6 +37,14 @@ * * The PL080 has a dual bus master, PL081 has a single master. * + * PL080S is a version modified by Samsung and used in S3C64xx SoCs. + * It differs in following aspects: + * - CH_CONFIG register at different offset, + * - separate CH_CONTROL2 register for transfer size, + * - bigger maximum transfer size, + * - 8-word aligned LLI, instead of 4-word, due to extra CCTL2 word, + * - no support for peripheral flow control. + * * Memory to peripheral transfer may be visualized as * Get data from memory to DMAC * Until no data left @@ -64,10 +73,7 @@ * - Peripheral flow control: the transfer size is ignored (and should be * zero). The data is transferred from the current LLI entry, until * after the final transfer signalled by LBREQ or LSREQ. The DMAC - * will then move to the next LLI entry. - * - * Global TODO: - * - Break out common code from arch/arm/mach-s3c64xx and share + * will then move to the next LLI entry. Unsupported by PL080S. */ #include #include @@ -100,12 +106,15 @@ struct pl08x_driver_data; * @nomadik: whether the channels have Nomadik security extension bits * that need to be checked for permission before use and some registers are * missing + * @pl080s: whether this version is a PL080S, which has separate register and + * LLI word for transfer size. */ struct vendor_data { u8 config_offset; u8 channels; bool dualmaster; bool nomadik; + bool pl080s; }; /** @@ -264,9 +273,11 @@ struct pl08x_driver_data { #define PL080_LLI_DST 1 #define PL080_LLI_LLI 2 #define PL080_LLI_CCTL 3 +#define PL080S_LLI_CCTL2 4 /* Total words in an LLI. */ #define PL080_LLI_WORDS 4 +#define PL080S_LLI_WORDS 8 /* * Number of LLIs in each LLI buffer allocated for one transfer @@ -340,17 +351,29 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch) static void pl08x_write_lli(struct pl08x_driver_data *pl08x, struct pl08x_phy_chan *phychan, const u32 *lli, u32 ccfg) { - dev_vdbg(&pl08x->adev->dev, - "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, " - "clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n", - phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST], - lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], ccfg); + if (pl08x->vd->pl080s) + dev_vdbg(&pl08x->adev->dev, + "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, " + "clli=0x%08x, cctl=0x%08x, cctl2=0x%08x, ccfg=0x%08x\n", + phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST], + lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], + lli[PL080S_LLI_CCTL2], ccfg); + else + dev_vdbg(&pl08x->adev->dev, + "WRITE channel %d: csrc=0x%08x, cdst=0x%08x, " + "clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n", + phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST], + lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], ccfg); writel_relaxed(lli[PL080_LLI_SRC], phychan->base + PL080_CH_SRC_ADDR); writel_relaxed(lli[PL080_LLI_DST], phychan->base + PL080_CH_DST_ADDR); writel_relaxed(lli[PL080_LLI_LLI], phychan->base + PL080_CH_LLI); writel_relaxed(lli[PL080_LLI_CCTL], phychan->base + PL080_CH_CONTROL); + if (pl08x->vd->pl080s) + writel_relaxed(lli[PL080S_LLI_CCTL2], + phychan->base + PL080S_CH_CONTROL2); + writel(ccfg, phychan->reg_config); } @@ -469,6 +492,24 @@ static inline u32 get_bytes_in_cctl(u32 cctl) return bytes; } +static inline u32 get_bytes_in_cctl_pl080s(u32 cctl, u32 cctl1) +{ + /* The source width defines the number of bytes */ + u32 bytes = cctl1 & PL080S_CONTROL_TRANSFER_SIZE_MASK; + + switch (cctl >> PL080_CONTROL_SWIDTH_SHIFT) { + case PL080_WIDTH_8BIT: + break; + case PL080_WIDTH_16BIT: + bytes *= 2; + break; + case PL080_WIDTH_32BIT: + bytes *= 4; + break; + } + return bytes; +} + /* The channel should be paused when calling this */ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) { @@ -494,7 +535,12 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) clli = readl(ch->base + PL080_CH_LLI) & ~PL080_LLI_LM_AHB2; /* First get the remaining bytes in the active transfer */ - bytes = get_bytes_in_cctl(readl(ch->base + PL080_CH_CONTROL)); + if (pl08x->vd->pl080s) + bytes = get_bytes_in_cctl_pl080s( + readl(ch->base + PL080_CH_CONTROL), + readl(ch->base + PL080S_CH_CONTROL2)); + else + bytes = get_bytes_in_cctl(readl(ch->base + PL080_CH_CONTROL)); if (!clli) return bytes; @@ -515,7 +561,12 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) llis_va_limit = llis_va + llis_max_words; for (; llis_va < llis_va_limit; llis_va += pl08x->lli_words) { - bytes += get_bytes_in_cctl(llis_va[PL080_LLI_CCTL]); + if (pl08x->vd->pl080s) + bytes += get_bytes_in_cctl_pl080s( + llis_va[PL080_LLI_CCTL], + llis_va[PL080S_LLI_CCTL2]); + else + bytes += get_bytes_in_cctl(llis_va[PL080_LLI_CCTL]); /* * A LLI pointer of 0 terminates the LLI list @@ -778,7 +829,7 @@ static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd, */ static void pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_lli_build_data *bd, - int num_llis, int len, u32 cctl) + int num_llis, int len, u32 cctl, u32 cctl2) { u32 offset = num_llis * pl08x->lli_words; u32 *llis_va = bd->txd->llis_va + offset; @@ -794,6 +845,8 @@ static void pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x, llis_va[PL080_LLI_LLI] = (llis_bus + sizeof(u32) * offset); llis_va[PL080_LLI_LLI] |= bd->lli_bus; llis_va[PL080_LLI_CCTL] = cctl; + if (pl08x->vd->pl080s) + llis_va[PL080S_LLI_CCTL2] = cctl2; if (cctl & PL080_CONTROL_SRC_INCR) bd->srcbus.addr += len; @@ -810,7 +863,7 @@ static inline void prep_byte_width_lli(struct pl08x_driver_data *pl08x, int num_llis, size_t *total_bytes) { *cctl = pl08x_cctl_bits(*cctl, 1, 1, len); - pl08x_fill_lli_for_desc(pl08x, bd, num_llis, len, *cctl); + pl08x_fill_lli_for_desc(pl08x, bd, num_llis, len, *cctl, len); (*total_bytes) += len; } @@ -820,16 +873,31 @@ static void pl08x_dump_lli(struct pl08x_driver_data *pl08x, { int i; - dev_vdbg(&pl08x->adev->dev, - "%-3s %-9s %-10s %-10s %-10s %s\n", - "lli", "", "csrc", "cdst", "clli", "cctl"); - for (i = 0; i < num_llis; i++) { + if (pl08x->vd->pl080s) { dev_vdbg(&pl08x->adev->dev, - "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", - i, llis_va, llis_va[PL080_LLI_SRC], - llis_va[PL080_LLI_DST], llis_va[PL080_LLI_LLI], - llis_va[PL080_LLI_CCTL]); - llis_va += pl08x->lli_words; + "%-3s %-9s %-10s %-10s %-10s %-10s %s\n", + "lli", "", "csrc", "cdst", "clli", "cctl", "cctl2"); + for (i = 0; i < num_llis; i++) { + dev_vdbg(&pl08x->adev->dev, + "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, llis_va, llis_va[PL080_LLI_SRC], + llis_va[PL080_LLI_DST], llis_va[PL080_LLI_LLI], + llis_va[PL080_LLI_CCTL], + llis_va[PL080S_LLI_CCTL2]); + llis_va += pl08x->lli_words; + } + } else { + dev_vdbg(&pl08x->adev->dev, + "%-3s %-9s %-10s %-10s %-10s %s\n", + "lli", "", "csrc", "cdst", "clli", "cctl"); + for (i = 0; i < num_llis; i++) { + dev_vdbg(&pl08x->adev->dev, + "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", + i, llis_va, llis_va[PL080_LLI_SRC], + llis_va[PL080_LLI_DST], llis_va[PL080_LLI_LLI], + llis_va[PL080_LLI_CCTL]); + llis_va += pl08x->lli_words; + } } } #else @@ -938,7 +1006,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, bd.dstbus.buswidth, 0); pl08x_fill_lli_for_desc(pl08x, &bd, num_llis++, - 0, cctl); + 0, cctl, 0); break; } @@ -1018,7 +1086,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, bd.dstbus.buswidth, tsize); pl08x_fill_lli_for_desc(pl08x, &bd, num_llis++, - lli_len, cctl); + lli_len, cctl, tsize); total_bytes += lli_len; } @@ -1332,6 +1400,7 @@ static int dma_set_runtime_config(struct dma_chan *chan, struct dma_slave_config *config) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + struct pl08x_driver_data *pl08x = plchan->host; if (!plchan->slave) return -EINVAL; @@ -1341,6 +1410,13 @@ static int dma_set_runtime_config(struct dma_chan *chan, config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; + if (config->device_fc && pl08x->vd->pl080s) { + dev_err(&pl08x->adev->dev, + "%s: PL080S does not support peripheral flow control\n", + __func__); + return -EINVAL; + } + plchan->cfg = *config; return 0; @@ -1930,7 +2006,10 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) pl08x->mem_buses = pl08x->pd->mem_buses; } - pl08x->lli_words = PL080_LLI_WORDS; + if (vd->pl080s) + pl08x->lli_words = PL080S_LLI_WORDS; + else + pl08x->lli_words = PL080_LLI_WORDS; tsfr_size = MAX_NUM_TSFR_LLIS * pl08x->lli_words * sizeof(u32); /* A DMA memory pool for LLIs, align on 1-byte boundary */ @@ -2040,8 +2119,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) amba_set_drvdata(adev, pl08x); init_pl08x_debugfs(pl08x); - dev_info(&pl08x->adev->dev, "DMA: PL%03x rev%u at 0x%08llx irq %d\n", - amba_part(adev), amba_rev(adev), + dev_info(&pl08x->adev->dev, "DMA: PL%03x%s rev%u at 0x%08llx irq %d\n", + amba_part(adev), pl08x->vd->pl080s ? "s" : "", amba_rev(adev), (unsigned long long)adev->res.start, adev->irq[0]); return 0; @@ -2082,6 +2161,12 @@ static struct vendor_data vendor_nomadik = { .nomadik = true, }; +static struct vendor_data vendor_pl080s = { + .config_offset = PL080S_CH_CONFIG, + .channels = 8, + .pl080s = true, +}; + static struct vendor_data vendor_pl081 = { .config_offset = PL080_CH_CONFIG, .channels = 2, @@ -2089,6 +2174,12 @@ static struct vendor_data vendor_pl081 = { }; static struct amba_id pl08x_ids[] = { + /* Samsung PL080S variant */ + { + .id = 0x0a141080, + .mask = 0xffffffff, + .data = &vendor_pl080s, + }, /* PL080 */ { .id = 0x00041080, diff --git a/include/linux/amba/pl080.h b/include/linux/amba/pl080.h index 3e7b62fbefbd..91b84a7f0539 100644 --- a/include/linux/amba/pl080.h +++ b/include/linux/amba/pl080.h @@ -87,6 +87,7 @@ #define PL080_CONTROL_SB_SIZE_MASK (0x7 << 12) #define PL080_CONTROL_SB_SIZE_SHIFT (12) #define PL080_CONTROL_TRANSFER_SIZE_MASK (0xfff << 0) +#define PL080S_CONTROL_TRANSFER_SIZE_MASK (0x1ffffff << 0) #define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0) #define PL080_BSIZE_1 (0x0) -- cgit v1.2.3