diff options
| -rw-r--r-- | drivers/dma/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/dma/amba-pl08x.c | 767 | ||||
| -rw-r--r-- | include/linux/amba/pl080.h | 83 |
3 files changed, 698 insertions, 158 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 24e8597b2c3e..67ab15aa98c3 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig | |||
| @@ -62,8 +62,10 @@ config AMBA_PL08X | |||
| 62 | select DMA_ENGINE | 62 | select DMA_ENGINE |
| 63 | select DMA_VIRTUAL_CHANNELS | 63 | select DMA_VIRTUAL_CHANNELS |
| 64 | help | 64 | help |
| 65 | Platform has a PL08x DMAC device | 65 | Say yes if your platform has a PL08x DMAC device which can |
| 66 | which can provide DMA engine support | 66 | provide DMA engine support. This includes the original ARM |
| 67 | PL080 and PL081, Samsungs PL080 derivative and Faraday | ||
| 68 | Technology's FTDMAC020 PL080 derivative. | ||
| 67 | 69 | ||
| 68 | config AMCC_PPC440SPE_ADMA | 70 | config AMCC_PPC440SPE_ADMA |
| 69 | tristate "AMCC PPC440SPe ADMA support" | 71 | tristate "AMCC PPC440SPe ADMA support" |
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6ddb029dac50..13cc95c0474c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c | |||
| @@ -1,9 +1,10 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Copyright (c) 2006 ARM Ltd. | 2 | * Copyright (c) 2006 ARM Ltd. |
| 3 | * Copyright (c) 2010 ST-Ericsson SA | 3 | * Copyright (c) 2010 ST-Ericsson SA |
| 4 | * Copyirght (c) 2017 Linaro Ltd. | ||
| 4 | * | 5 | * |
| 5 | * Author: Peter Pearse <peter.pearse@arm.com> | 6 | * Author: Peter Pearse <peter.pearse@arm.com> |
| 6 | * Author: Linus Walleij <linus.walleij@stericsson.com> | 7 | * Author: Linus Walleij <linus.walleij@linaro.org> |
| 7 | * | 8 | * |
| 8 | * This program is free software; you can redistribute it and/or modify it | 9 | * This program is free software; you can redistribute it and/or modify it |
| 9 | * under the terms of the GNU General Public License as published by the Free | 10 | * under the terms of the GNU General Public License as published by the Free |
| @@ -110,11 +111,12 @@ struct pl08x_driver_data; | |||
| 110 | * @channels: the number of channels available in this variant | 111 | * @channels: the number of channels available in this variant |
| 111 | * @signals: the number of request signals available from the hardware | 112 | * @signals: the number of request signals available from the hardware |
| 112 | * @dualmaster: whether this version supports dual AHB masters or not. | 113 | * @dualmaster: whether this version supports dual AHB masters or not. |
| 113 | * @nomadik: whether the channels have Nomadik security extension bits | 114 | * @nomadik: whether this variant is a ST Microelectronics Nomadik, where the |
| 114 | * that need to be checked for permission before use and some registers are | 115 | * channels have Nomadik security extension bits that need to be checked |
| 115 | * missing | 116 | * for permission before use and some registers are missing |
| 116 | * @pl080s: whether this version is a PL080S, which has separate register and | 117 | * @pl080s: whether this variant is a Samsung PL080S, which has separate |
| 117 | * LLI word for transfer size. | 118 | * register and LLI word for transfer size. |
| 119 | * @ftdmac020: whether this variant is a Faraday Technology FTDMAC020 | ||
| 118 | * @max_transfer_size: the maximum single element transfer size for this | 120 | * @max_transfer_size: the maximum single element transfer size for this |
| 119 | * PL08x variant. | 121 | * PL08x variant. |
| 120 | */ | 122 | */ |
| @@ -125,6 +127,7 @@ struct vendor_data { | |||
| 125 | bool dualmaster; | 127 | bool dualmaster; |
| 126 | bool nomadik; | 128 | bool nomadik; |
| 127 | bool pl080s; | 129 | bool pl080s; |
| 130 | bool ftdmac020; | ||
| 128 | u32 max_transfer_size; | 131 | u32 max_transfer_size; |
| 129 | }; | 132 | }; |
| 130 | 133 | ||
| @@ -148,19 +151,34 @@ struct pl08x_bus_data { | |||
| 148 | * @id: physical index to this channel | 151 | * @id: physical index to this channel |
| 149 | * @base: memory base address for this physical channel | 152 | * @base: memory base address for this physical channel |
| 150 | * @reg_config: configuration address for this physical channel | 153 | * @reg_config: configuration address for this physical channel |
| 154 | * @reg_control: control address for this physical channel | ||
| 155 | * @reg_src: transfer source address register | ||
| 156 | * @reg_dst: transfer destination address register | ||
| 157 | * @reg_lli: transfer LLI address register | ||
| 158 | * @reg_busy: if the variant has a special per-channel busy register, | ||
| 159 | * this contains a pointer to it | ||
| 151 | * @lock: a lock to use when altering an instance of this struct | 160 | * @lock: a lock to use when altering an instance of this struct |
| 152 | * @serving: the virtual channel currently being served by this physical | 161 | * @serving: the virtual channel currently being served by this physical |
| 153 | * channel | 162 | * channel |
| 154 | * @locked: channel unavailable for the system, e.g. dedicated to secure | 163 | * @locked: channel unavailable for the system, e.g. dedicated to secure |
| 155 | * world | 164 | * world |
| 165 | * @ftdmac020: channel is on a FTDMAC020 | ||
| 166 | * @pl080s: channel is on a PL08s | ||
| 156 | */ | 167 | */ |
| 157 | struct pl08x_phy_chan { | 168 | struct pl08x_phy_chan { |
| 158 | unsigned int id; | 169 | unsigned int id; |
| 159 | void __iomem *base; | 170 | void __iomem *base; |
| 160 | void __iomem *reg_config; | 171 | void __iomem *reg_config; |
| 172 | void __iomem *reg_control; | ||
| 173 | void __iomem *reg_src; | ||
| 174 | void __iomem *reg_dst; | ||
| 175 | void __iomem *reg_lli; | ||
| 176 | void __iomem *reg_busy; | ||
| 161 | spinlock_t lock; | 177 | spinlock_t lock; |
| 162 | struct pl08x_dma_chan *serving; | 178 | struct pl08x_dma_chan *serving; |
| 163 | bool locked; | 179 | bool locked; |
| 180 | bool ftdmac020; | ||
| 181 | bool pl080s; | ||
| 164 | }; | 182 | }; |
| 165 | 183 | ||
| 166 | /** | 184 | /** |
| @@ -362,10 +380,24 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch) | |||
| 362 | { | 380 | { |
| 363 | unsigned int val; | 381 | unsigned int val; |
| 364 | 382 | ||
| 383 | /* If we have a special busy register, take a shortcut */ | ||
| 384 | if (ch->reg_busy) { | ||
| 385 | val = readl(ch->reg_busy); | ||
| 386 | return !!(val & BIT(ch->id)); | ||
| 387 | } | ||
| 365 | val = readl(ch->reg_config); | 388 | val = readl(ch->reg_config); |
| 366 | return val & PL080_CONFIG_ACTIVE; | 389 | return val & PL080_CONFIG_ACTIVE; |
| 367 | } | 390 | } |
| 368 | 391 | ||
| 392 | /* | ||
| 393 | * pl08x_write_lli() - Write an LLI into the DMA controller. | ||
| 394 | * | ||
| 395 | * The PL08x derivatives support linked lists, but the first item of the | ||
| 396 | * list containing the source, destination, control word and next LLI is | ||
| 397 | * ignored. Instead the driver has to write those values directly into the | ||
| 398 | * SRC, DST, LLI and control registers. On FTDMAC020 also the SIZE | ||
| 399 | * register need to be set up for the first transfer. | ||
| 400 | */ | ||
| 369 | static void pl08x_write_lli(struct pl08x_driver_data *pl08x, | 401 | static void pl08x_write_lli(struct pl08x_driver_data *pl08x, |
| 370 | struct pl08x_phy_chan *phychan, const u32 *lli, u32 ccfg) | 402 | struct pl08x_phy_chan *phychan, const u32 *lli, u32 ccfg) |
| 371 | { | 403 | { |
| @@ -383,11 +415,112 @@ static void pl08x_write_lli(struct pl08x_driver_data *pl08x, | |||
| 383 | phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST], | 415 | phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST], |
| 384 | lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], ccfg); | 416 | lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], ccfg); |
| 385 | 417 | ||
| 386 | writel_relaxed(lli[PL080_LLI_SRC], phychan->base + PL080_CH_SRC_ADDR); | 418 | writel_relaxed(lli[PL080_LLI_SRC], phychan->reg_src); |
| 387 | writel_relaxed(lli[PL080_LLI_DST], phychan->base + PL080_CH_DST_ADDR); | 419 | writel_relaxed(lli[PL080_LLI_DST], phychan->reg_dst); |
| 388 | writel_relaxed(lli[PL080_LLI_LLI], phychan->base + PL080_CH_LLI); | 420 | writel_relaxed(lli[PL080_LLI_LLI], phychan->reg_lli); |
| 389 | writel_relaxed(lli[PL080_LLI_CCTL], phychan->base + PL080_CH_CONTROL); | ||
| 390 | 421 | ||
| 422 | /* | ||
| 423 | * The FTMAC020 has a different layout in the CCTL word of the LLI | ||
| 424 | * and the CCTL register which is split in CSR and SIZE registers. | ||
| 425 | * Convert the LLI item CCTL into the proper values to write into | ||
| 426 | * the CSR and SIZE registers. | ||
| 427 | */ | ||
| 428 | if (phychan->ftdmac020) { | ||
| 429 | u32 llictl = lli[PL080_LLI_CCTL]; | ||
| 430 | u32 val = 0; | ||
| 431 | |||
| 432 | /* Write the transfer size (12 bits) to the size register */ | ||
| 433 | writel_relaxed(llictl & FTDMAC020_LLI_TRANSFER_SIZE_MASK, | ||
| 434 | phychan->base + FTDMAC020_CH_SIZE); | ||
| 435 | /* | ||
| 436 | * Then write the control bits 28..16 to the control register | ||
| 437 | * by shuffleing the bits around to where they are in the | ||
| 438 | * main register. The mapping is as follows: | ||
| 439 | * Bit 28: TC_MSK - mask on all except last LLI | ||
| 440 | * Bit 27..25: SRC_WIDTH | ||
| 441 | * Bit 24..22: DST_WIDTH | ||
| 442 | * Bit 21..20: SRCAD_CTRL | ||
| 443 | * Bit 19..17: DSTAD_CTRL | ||
| 444 | * Bit 17: SRC_SEL | ||
| 445 | * Bit 16: DST_SEL | ||
| 446 | */ | ||
| 447 | if (llictl & FTDMAC020_LLI_TC_MSK) | ||
| 448 | val |= FTDMAC020_CH_CSR_TC_MSK; | ||
| 449 | val |= ((llictl & FTDMAC020_LLI_SRC_WIDTH_MSK) >> | ||
| 450 | (FTDMAC020_LLI_SRC_WIDTH_SHIFT - | ||
| 451 | FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT)); | ||
| 452 | val |= ((llictl & FTDMAC020_LLI_DST_WIDTH_MSK) >> | ||
| 453 | (FTDMAC020_LLI_DST_WIDTH_SHIFT - | ||
| 454 | FTDMAC020_CH_CSR_DST_WIDTH_SHIFT)); | ||
| 455 | val |= ((llictl & FTDMAC020_LLI_SRCAD_CTL_MSK) >> | ||
| 456 | (FTDMAC020_LLI_SRCAD_CTL_SHIFT - | ||
| 457 | FTDMAC020_CH_CSR_SRCAD_CTL_SHIFT)); | ||
| 458 | val |= ((llictl & FTDMAC020_LLI_DSTAD_CTL_MSK) >> | ||
| 459 | (FTDMAC020_LLI_DSTAD_CTL_SHIFT - | ||
| 460 | FTDMAC020_CH_CSR_DSTAD_CTL_SHIFT)); | ||
| 461 | if (llictl & FTDMAC020_LLI_SRC_SEL) | ||
| 462 | val |= FTDMAC020_CH_CSR_SRC_SEL; | ||
| 463 | if (llictl & FTDMAC020_LLI_DST_SEL) | ||
| 464 | val |= FTDMAC020_CH_CSR_DST_SEL; | ||
| 465 | |||
| 466 | /* | ||
| 467 | * Set up the bits that exist in the CSR but are not | ||
| 468 | * part the LLI, i.e. only gets written to the control | ||
| 469 | * register right here. | ||
| 470 | * | ||
| 471 | * FIXME: do not just handle memcpy, also handle slave DMA. | ||
| 472 | */ | ||
| 473 | switch (pl08x->pd->memcpy_burst_size) { | ||
| 474 | default: | ||
| 475 | case PL08X_BURST_SZ_1: | ||
| 476 | val |= PL080_BSIZE_1 << | ||
| 477 | FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; | ||
| 478 | break; | ||
| 479 | case PL08X_BURST_SZ_4: | ||
