// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2006 ARM Ltd. * Copyright (c) 2010 ST-Ericsson SA * Copyright (c) 2017 Linaro Ltd. * * Author: Peter Pearse <peter.pearse@arm.com> * Author: Linus Walleij <linus.walleij@linaro.org> * * 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. * * The PL080 has 8 channels available for simultaneous use, and the PL081 * has only two channels. So on these DMA controllers the number of channels * and the number of incoming DMA signals are two totally different things. * It is usually not possible to theoretically handle all physical signals, * so a multiplexing scheme with possible denial of use is necessary. * * 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 * On burst request from peripheral * Destination burst from DMAC to peripheral * Clear burst request * Raise terminal count interrupt * * For peripherals with a FIFO: * Source burst size == half the depth of the peripheral FIFO * Destination burst size == the depth of the peripheral FIFO * * (Bursts are irrelevant for mem to mem transfers - there are no burst * signals, the DMA controller will simply facilitate its AHB master.) * * ASSUMES default (little) endianness for DMA transfers * * The PL08x has two flow control settings: * - DMAC flow control: the transfer size defines the number of transfers * which occur for the current LLI entry, and the DMAC raises TC at the * end of every LLI entry. Observed behaviour shows the DMAC listening * to both the BREQ and SREQ signals (contrary to documented), * transferring data if either is active. The LBREQ and LSREQ signals * are ignored. * * - 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. Unsupported by PL080S.
*/ #include <linux/amba/bus.h> #include <linux/amba/pl08x.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/dmaengine.h> #include <linux/dmapool.h> #include <linux/dma-mapping.h> #include <linux/export.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_dma.h> #include <linux/pm_runtime.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/amba/pl080.h>
/** * struct vendor_data - vendor-specific config parameters for PL08x derivatives * @config_offset: offset to the configuration register * @channels: the number of channels available in this variant * @signals: the number of request signals available from the hardware * @dualmaster: whether this version supports dual AHB masters or not. * @nomadik: whether this variant is a ST Microelectronics Nomadik, where the * channels have Nomadik security extension bits that need to be checked * for permission before use and some registers are missing * @pl080s: whether this variant is a Samsung PL080S, which has separate * register and LLI word for transfer size. * @ftdmac020: whether this variant is a Faraday Technology FTDMAC020 * @max_transfer_size: the maximum single element transfer size for this * PL08x variant.
*/ struct vendor_data {
u8 config_offset;
u8 channels;
u8 signals; bool dualmaster; bool nomadik; bool pl080s; bool ftdmac020;
u32 max_transfer_size;
};
/** * struct pl08x_bus_data - information of source or destination * busses for a transfer * @addr: current address * @maxwidth: the maximum width of a transfer on this bus * @buswidth: the width of this bus in bytes: 1, 2 or 4
*/ struct pl08x_bus_data {
dma_addr_t addr;
u8 maxwidth;
u8 buswidth;
};
/** * struct pl08x_phy_chan - holder for the physical channels * @id: physical index to this channel * @base: memory base address for this physical channel * @reg_config: configuration address for this physical channel * @reg_control: control address for this physical channel * @reg_src: transfer source address register * @reg_dst: transfer destination address register * @reg_lli: transfer LLI address register * @reg_busy: if the variant has a special per-channel busy register, * this contains a pointer to it * @lock: a lock to use when altering an instance of this struct * @serving: the virtual channel currently being served by this physical * channel * @locked: channel unavailable for the system, e.g. dedicated to secure * world * @ftdmac020: channel is on a FTDMAC020 * @pl080s: channel is on a PL08s
*/ struct pl08x_phy_chan { unsignedint id; void __iomem *base; void __iomem *reg_config; void __iomem *reg_control; void __iomem *reg_src; void __iomem *reg_dst; void __iomem *reg_lli; void __iomem *reg_busy;
spinlock_t lock; struct pl08x_dma_chan *serving; bool locked; bool ftdmac020; bool pl080s;
};
/** * struct pl08x_sg - structure containing data per sg * @src_addr: src address of sg * @dst_addr: dst address of sg * @len: transfer len in bytes * @node: node for txd's dsg_list
*/ struct pl08x_sg {
dma_addr_t src_addr;
dma_addr_t dst_addr;
size_t len; struct list_head node;
};
/** * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor * @vd: virtual DMA descriptor * @dsg_list: list of children sg's * @llis_bus: DMA memory address (physical) start for the LLIs * @llis_va: virtual memory address start for the LLIs * @cctl: control reg values for current txd * @ccfg: config reg values for current txd * @done: this marks completed descriptors, which should not have their * mux released. * @cyclic: indicate cyclic transfers
*/ struct pl08x_txd { struct virt_dma_desc vd; struct list_head dsg_list;
dma_addr_t llis_bus;
u32 *llis_va; /* Default cctl value for LLIs */
u32 cctl; /* * Settings to be put into the physical channel when we * trigger this txd. Other registers are in llis_va[0].
*/
u32 ccfg; bool done; bool cyclic;
};
/** * enum pl08x_dma_chan_state - holds the PL08x specific virtual channel * states * @PL08X_CHAN_IDLE: the channel is idle * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport * channel and is running a transfer on it * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport * channel, but the transfer is currently paused * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport * channel to become available (only pertains to memcpy channels)
*/ enum pl08x_dma_chan_state {
PL08X_CHAN_IDLE,
PL08X_CHAN_RUNNING,
PL08X_CHAN_PAUSED,
PL08X_CHAN_WAITING,
};
/** * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel * @vc: wrapped virtual channel * @phychan: the physical channel utilized by this channel, if there is one * @name: name of channel * @cd: channel platform data * @cfg: slave configuration * @at: active transaction on this channel * @host: a pointer to the host (internal use) * @state: whether the channel is idle, paused, running etc * @slave: whether this channel is a device (slave) or for memcpy * @signal: the physical DMA request signal which this channel is using * @mux_use: count of descriptors using this DMA request signal setting * @waiting_at: time in jiffies when this channel moved to waiting state
*/ struct pl08x_dma_chan { struct virt_dma_chan vc; struct pl08x_phy_chan *phychan; constchar *name; struct pl08x_channel_data *cd; struct dma_slave_config cfg; struct pl08x_txd *at; struct pl08x_driver_data *host; enum pl08x_dma_chan_state state; bool slave; int signal; unsigned mux_use; unsignedlong waiting_at;
};
/** * struct pl08x_driver_data - the local state holder for the PL08x * @slave: optional slave engine for this instance * @memcpy: memcpy engine for this instance * @has_slave: the PL08x has a slave engine (routed signals) * @base: virtual memory base (remapped) for the PL08x * @adev: the corresponding AMBA (PrimeCell) bus entry * @vd: vendor data for this PL08x variant * @pd: platform data passed in from the platform/machine * @phy_chans: array of data for the physical channels * @pool: a pool for the LLI descriptors * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI * fetches * @mem_buses: set to indicate memory transfers on AHB2. * @lli_words: how many words are used in each LLI item for this variant
*/ struct pl08x_driver_data { struct dma_device slave; struct dma_device memcpy; bool has_slave; void __iomem *base; struct amba_device *adev; conststruct vendor_data *vd; struct pl08x_platform_data *pd; struct pl08x_phy_chan *phy_chans; struct dma_pool *pool;
u8 lli_buses;
u8 mem_buses;
u8 lli_words;
};
/* * PL08X specific defines
*/
/* The order of words in an LLI. */ #define PL080_LLI_SRC 0 #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 * (maximum times we call dma_pool_alloc on this pool without freeing)
*/ #define MAX_NUM_TSFR_LLIS 512 #define PL08X_ALIGN 8
/* * Mux handling. * * This gives us the DMA request input to the PL08x primecell which the * peripheral described by the channel data will be routed to, possibly * via a board/SoC specific external MUX. One important point to note * here is that this does not depend on the physical channel.
*/ staticint pl08x_request_mux(struct pl08x_dma_chan *plchan)
{ conststruct pl08x_platform_data *pd = plchan->host->pd; int ret;
if (plchan->mux_use++ == 0 && pd->get_xfer_signal) {
ret = pd->get_xfer_signal(plchan->cd); if (ret < 0) {
plchan->mux_use = 0; return ret;
}
/* Whether a certain channel is busy or not */ staticint pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
{ unsignedint val;
/* If we have a special busy register, take a shortcut */ if (ch->reg_busy) {
val = readl(ch->reg_busy); return !!(val & BIT(ch->id));
}
val = readl(ch->reg_config); return val & PL080_CONFIG_ACTIVE;
}
/* * pl08x_write_lli() - Write an LLI into the DMA controller. * * The PL08x derivatives support linked lists, but the first item of the * list containing the source, destination, control word and next LLI is * ignored. Instead the driver has to write those values directly into the * SRC, DST, LLI and control registers. On FTDMAC020 also the SIZE * register need to be set up for the first transfer.
*/ staticvoid pl08x_write_lli(struct pl08x_driver_data *pl08x, struct pl08x_phy_chan *phychan, const u32 *lli, u32 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);
/* * The FTMAC020 has a different layout in the CCTL word of the LLI * and the CCTL register which is split in CSR and SIZE registers. * Convert the LLI item CCTL into the proper values to write into * the CSR and SIZE registers.
*/ if (phychan->ftdmac020) {
u32 llictl = lli[PL080_LLI_CCTL];
u32 val = 0;
/* Write the transfer size (12 bits) to the size register */
writel_relaxed(llictl & FTDMAC020_LLI_TRANSFER_SIZE_MASK,
phychan->base + FTDMAC020_CH_SIZE); /* * Then write the control bits 28..16 to the control register * by shuffleing the bits around to where they are in the * main register. The mapping is as follows: * Bit 28: TC_MSK - mask on all except last LLI * Bit 27..25: SRC_WIDTH * Bit 24..22: DST_WIDTH * Bit 21..20: SRCAD_CTRL * Bit 19..17: DSTAD_CTRL * Bit 17: SRC_SEL * Bit 16: DST_SEL
*/ if (llictl & FTDMAC020_LLI_TC_MSK)
val |= FTDMAC020_CH_CSR_TC_MSK;
val |= ((llictl & FTDMAC020_LLI_SRC_WIDTH_MSK) >>
(FTDMAC020_LLI_SRC_WIDTH_SHIFT -
FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT));
val |= ((llictl & FTDMAC020_LLI_DST_WIDTH_MSK) >>
(FTDMAC020_LLI_DST_WIDTH_SHIFT -
FTDMAC020_CH_CSR_DST_WIDTH_SHIFT));
val |= ((llictl & FTDMAC020_LLI_SRCAD_CTL_MSK) >>
(FTDMAC020_LLI_SRCAD_CTL_SHIFT -
FTDMAC020_CH_CSR_SRCAD_CTL_SHIFT));
val |= ((llictl & FTDMAC020_LLI_DSTAD_CTL_MSK) >>
(FTDMAC020_LLI_DSTAD_CTL_SHIFT -
FTDMAC020_CH_CSR_DSTAD_CTL_SHIFT)); if (llictl & FTDMAC020_LLI_SRC_SEL)
val |= FTDMAC020_CH_CSR_SRC_SEL; if (llictl & FTDMAC020_LLI_DST_SEL)
val |= FTDMAC020_CH_CSR_DST_SEL;
/* * Set up the bits that exist in the CSR but are not * part the LLI, i.e. only gets written to the control * register right here. * * FIXME: do not just handle memcpy, also handle slave DMA.
*/ switch (pl08x->pd->memcpy_burst_size) { default: case PL08X_BURST_SZ_1:
val |= PL080_BSIZE_1 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_4:
val |= PL080_BSIZE_4 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_8:
val |= PL080_BSIZE_8 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_16:
val |= PL080_BSIZE_16 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_32:
val |= PL080_BSIZE_32 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_64:
val |= PL080_BSIZE_64 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_128:
val |= PL080_BSIZE_128 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break; case PL08X_BURST_SZ_256:
val |= PL080_BSIZE_256 <<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT; break;
}
/* Protection flags */ if (pl08x->pd->memcpy_prot_buff)
val |= FTDMAC020_CH_CSR_PROT2; if (pl08x->pd->memcpy_prot_cache)
val |= FTDMAC020_CH_CSR_PROT3; /* We are the kernel, so we are in privileged mode */
val |= FTDMAC020_CH_CSR_PROT1;
writel_relaxed(val, phychan->reg_control);
} else { /* Bits are just identical */
writel_relaxed(lli[PL080_LLI_CCTL], phychan->reg_control);
}
/* Second control word on the PL080s */ if (pl08x->vd->pl080s)
writel_relaxed(lli[PL080S_LLI_CCTL2],
phychan->base + PL080S_CH_CONTROL2);
writel(ccfg, phychan->reg_config);
}
/* * Set the initial DMA register values i.e. those for the first LLI * The next LLI pointer and the configuration interrupt bit have * been set when the LLIs were constructed. Poke them into the hardware * and start the transfer.
*/ staticvoid pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
{ struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_phy_chan *phychan = plchan->phychan; struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc); struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
u32 val;
list_del(&txd->vd.node);
plchan->at = txd;
/* Wait for channel inactive */ while (pl08x_phy_channel_busy(phychan))
cpu_relax();
/* Enable the DMA channel */ /* Do not access config register until channel shows as disabled */ while (readl(pl08x->base + PL080_EN_CHAN) & BIT(phychan->id))
cpu_relax();
/* Do not access config register until channel shows as inactive */ if (phychan->ftdmac020) {
val = readl(phychan->reg_config); while (val & FTDMAC020_CH_CFG_BUSY)
val = readl(phychan->reg_config);
val = readl(phychan->reg_control); while (val & FTDMAC020_CH_CSR_EN)
val = readl(phychan->reg_control);
writel(val | FTDMAC020_CH_CSR_EN,
phychan->reg_control);
} else {
val = readl(phychan->reg_config); while ((val & PL080_CONFIG_ACTIVE) ||
(val & PL080_CONFIG_ENABLE))
val = readl(phychan->reg_config);
/* * Pause the channel by setting the HALT bit. * * For M->P transfers, pause the DMAC first and then stop the peripheral - * the FIFO can only drain if the peripheral is still requesting data. * (note: this can still timeout if the DMAC FIFO never drains of data.) * * For P->M transfers, disable the peripheral first to stop it filling * the DMAC FIFO, and then pause the DMAC.
*/ staticvoid pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
{
u32 val; int timeout;
if (ch->ftdmac020) { /* Use the enable bit on the FTDMAC020 */
val = readl(ch->reg_control);
val &= ~FTDMAC020_CH_CSR_EN;
writel(val, ch->reg_control); return;
}
/* Set the HALT bit and wait for the FIFO to drain */
val = readl(ch->reg_config);
val |= PL080_CONFIG_HALT;
writel(val, ch->reg_config);
/* Wait for channel inactive */ for (timeout = 1000; timeout; timeout--) { if (!pl08x_phy_channel_busy(ch)) break;
udelay(1);
} if (pl08x_phy_channel_busy(ch))
pr_err("pl08x: channel%u timeout waiting for pause\n", ch->id);
}
/* Use the enable bit on the FTDMAC020 */ if (ch->ftdmac020) {
val = readl(ch->reg_control);
val |= FTDMAC020_CH_CSR_EN;
writel(val, ch->reg_control); return;
}
/* Clear the HALT bit */
val = readl(ch->reg_config);
val &= ~PL080_CONFIG_HALT;
writel(val, ch->reg_config);
}
/* * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and * clears any pending interrupt status. This should not be used for * an on-going transfer, but as a method of shutting down a channel * (eg, when it's no longer used) or terminating a transfer.
*/ staticvoid pl08x_terminate_phy_chan(struct pl08x_driver_data *pl08x, struct pl08x_phy_chan *ch)
{
u32 val;
/* The layout for the FTDMAC020 is different */ if (ch->ftdmac020) { /* Disable all interrupts */
val = readl(ch->reg_config);
val |= (FTDMAC020_CH_CFG_INT_ABT_MASK |
FTDMAC020_CH_CFG_INT_ERR_MASK |
FTDMAC020_CH_CFG_INT_TC_MASK);
writel(val, ch->reg_config);
/* Abort and disable channel */
val = readl(ch->reg_control);
val &= ~FTDMAC020_CH_CSR_EN;
val |= FTDMAC020_CH_CSR_ABT;
writel(val, ch->reg_control);
if (ch->ftdmac020) {
bytes = readl(ch->base + FTDMAC020_CH_SIZE);
val = readl(ch->reg_control);
val &= FTDMAC020_CH_CSR_SRC_WIDTH_MSK;
val >>= FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT;
} elseif (ch->pl080s) {
val = readl(ch->base + PL080S_CH_CONTROL2);
bytes = val & PL080S_CONTROL_TRANSFER_SIZE_MASK;
val = readl(ch->reg_control);
val &= PL080_CONTROL_SWIDTH_MASK;
val >>= PL080_CONTROL_SWIDTH_SHIFT;
} else { /* Plain PL08x */
val = readl(ch->reg_control);
bytes = val & PL080_CONTROL_TRANSFER_SIZE_MASK;
val &= PL080_CONTROL_SWIDTH_MASK;
val >>= PL080_CONTROL_SWIDTH_SHIFT;
}
switch (val) { case PL080_WIDTH_8BIT: break; case PL080_WIDTH_16BIT:
bytes *= 2; break; case PL080_WIDTH_32BIT:
bytes *= 4; break;
} return bytes;
}
if (ch->ftdmac020) {
val = llis_va[PL080_LLI_CCTL];
bytes = val & FTDMAC020_LLI_TRANSFER_SIZE_MASK;
val = llis_va[PL080_LLI_CCTL];
val &= FTDMAC020_LLI_SRC_WIDTH_MSK;
val >>= FTDMAC020_LLI_SRC_WIDTH_SHIFT;
} elseif (ch->pl080s) {
val = llis_va[PL080S_LLI_CCTL2];
bytes = val & PL080S_CONTROL_TRANSFER_SIZE_MASK;
val = llis_va[PL080_LLI_CCTL];
val &= PL080_CONTROL_SWIDTH_MASK;
val >>= PL080_CONTROL_SWIDTH_SHIFT;
} else { /* Plain PL08x */
val = llis_va[PL080_LLI_CCTL];
bytes = val & PL080_CONTROL_TRANSFER_SIZE_MASK;
val &= PL080_CONTROL_SWIDTH_MASK;
val >>= PL080_CONTROL_SWIDTH_SHIFT;
}
switch (val) { 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)
{ struct pl08x_driver_data *pl08x = plchan->host; const u32 *llis_va, *llis_va_limit; struct pl08x_phy_chan *ch;
dma_addr_t llis_bus; struct pl08x_txd *txd;
u32 llis_max_words;
size_t bytes;
u32 clli;
ch = plchan->phychan;
txd = plchan->at;
if (!ch || !txd) return 0;
/* * Follow the LLIs to get the number of remaining * bytes in the currently active transaction.
*/
clli = readl(ch->reg_lli) & ~PL080_LLI_LM_AHB2;
/* First get the remaining bytes in the active transfer */
bytes = get_bytes_in_phy_channel(ch);
/* * A LLI pointer going backward terminates the LLI list
*/ if (llis_va[PL080_LLI_LLI] <= clli) break;
}
return bytes;
}
/* * Allocate a physical channel for a virtual channel * * Try to locate a physical channel to be used for this transfer. If all * are taken return NULL and the requester will have to cope by using * some fallback PIO mode or retrying later.
*/ staticstruct pl08x_phy_chan *
pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, struct pl08x_dma_chan *virt_chan)
{ struct pl08x_phy_chan *ch = NULL; unsignedlong flags; int i;
for (i = 0; i < pl08x->vd->channels; i++) {
ch = &pl08x->phy_chans[i];
if (i == pl08x->vd->channels) { /* No physical channel available, cope with it */ return NULL;
}
return ch;
}
/* Mark the physical channel as free. Note, this write is atomic. */ staticinlinevoid pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, struct pl08x_phy_chan *ch)
{
ch->serving = NULL;
}
/* * Try to allocate a physical channel. When successful, assign it to * this virtual channel, and initiate the next descriptor. The * virtual channel lock must be held at this point.
*/ staticvoid pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan)
{ struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_phy_chan *ch;
ch = pl08x_get_phy_channel(pl08x, plchan); if (!ch) {
dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
plchan->state = PL08X_CHAN_WAITING;
plchan->waiting_at = jiffies; return;
}
dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
ch->id, plchan->name);
dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n",
ch->id, plchan->name);
/* * We do this without taking the lock; we're really only concerned * about whether this pointer is NULL or not, and we're guaranteed * that this will only be called when it _already_ is non-NULL.
*/
ch->serving = plchan;
plchan->phychan = ch;
plchan->state = PL08X_CHAN_RUNNING;
pl08x_start_next_txd(plchan);
}
/* * Free a physical DMA channel, potentially reallocating it to another * virtual channel if we have any pending.
*/ staticvoid pl08x_phy_free(struct pl08x_dma_chan *plchan)
{ struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_dma_chan *p, *next; unsignedlong waiting_at;
retry:
next = NULL;
waiting_at = jiffies;
/* * Find a waiting virtual channel for the next transfer. * To be fair, time when each channel reached waiting state is compared * to select channel that is waiting for the longest time.
*/
list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node) if (p->state == PL08X_CHAN_WAITING &&
p->waiting_at <= waiting_at) {
next = p;
waiting_at = p->waiting_at;
}
if (!next && pl08x->has_slave) {
list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node) if (p->state == PL08X_CHAN_WAITING &&
p->waiting_at <= waiting_at) {
next = p;
waiting_at = p->waiting_at;
}
}
/* Ensure that the physical channel is stopped */
pl08x_terminate_phy_chan(pl08x, plchan->phychan);
if (next) { bool success;
/* * Eww. We know this isn't going to deadlock * but lockdep probably doesn't.
*/
spin_lock(&next->vc.lock); /* Re-check the state now that we have the lock */
success = next->state == PL08X_CHAN_WAITING; if (success)
pl08x_phy_reassign_start(plchan->phychan, next);
spin_unlock(&next->vc.lock);
/* If the state changed, try to find another channel */ if (!success) goto retry;
} else { /* No more jobs, so free up the physical channel */
pl08x_put_phy_channel(pl08x, plchan->phychan);
}
/* * Remove all src, dst and transfer size bits, then set the * width and size according to the parameters. The bit offsets * are different in the FTDMAC020 so we need to accound for this.
*/ if (pl08x->vd->ftdmac020) {
retbits &= ~FTDMAC020_LLI_DST_WIDTH_MSK;
retbits &= ~FTDMAC020_LLI_SRC_WIDTH_MSK;
retbits &= ~FTDMAC020_LLI_TRANSFER_SIZE_MASK;
/* * Autoselect a master bus to use for the transfer. Slave will be the chosen as * victim in case src & dest are not similarly aligned. i.e. If after aligning * masters address with width requirements of transfer (by sending few byte by * byte data), slave is still not aligned, then its width will be reduced to * BYTE. * - prefers the destination bus if both available * - prefers bus with fixed address (i.e. peripheral)
*/ staticvoid pl08x_choose_master_bus(struct pl08x_driver_data *pl08x, struct pl08x_lli_build_data *bd, struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus,
u32 cctl)
{ bool dst_incr; bool src_incr;
/* * The FTDMAC020 only supports memory-to-memory transfer, so * source and destination always increase.
*/ if (pl08x->vd->ftdmac020) {
dst_incr = true;
src_incr = true;
} else {
dst_incr = !!(cctl & PL080_CONTROL_DST_INCR);
src_incr = !!(cctl & PL080_CONTROL_SRC_INCR);
}
/* * If either bus is not advancing, i.e. it is a peripheral, that * one becomes master
*/ if (!dst_incr) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
} elseif (!src_incr) {
*mbus = &bd->srcbus;
*sbus = &bd->dstbus;
} else { if (bd->dstbus.buswidth >= bd->srcbus.buswidth) {
*mbus = &bd->dstbus;
*sbus = &bd->srcbus;
} else {
*mbus = &bd->srcbus;
*sbus = &bd->dstbus;
}
}
}
/* * Fills in one LLI for a certain transfer descriptor and advance the counter
*/ staticvoid pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_lli_build_data *bd, int num_llis, int len, u32 cctl, u32 cctl2)
{
u32 offset = num_llis * pl08x->lli_words;
u32 *llis_va = bd->txd->llis_va + offset;
dma_addr_t llis_bus = bd->txd->llis_bus;
BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
/* Advance the offset to next LLI. */
offset += pl08x->lli_words;
if (pl08x->vd->ftdmac020) { /* FIXME: only memcpy so far so both increase */
bd->srcbus.addr += len;
bd->dstbus.addr += len;
} else { if (cctl & PL080_CONTROL_SRC_INCR)
bd->srcbus.addr += len; if (cctl & PL080_CONTROL_DST_INCR)
bd->dstbus.addr += len;
}
/* * This fills in the table of LLIs for the transfer descriptor * Note that we assume we never have to change the burst sizes * Return 0 for error
*/ staticint pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_txd *txd)
{ struct pl08x_bus_data *mbus, *sbus; struct pl08x_lli_build_data bd; int num_llis = 0;
u32 cctl, early_bytes = 0;
size_t max_bytes_per_lli, total_bytes;
u32 *llis_va, *last_lli; struct pl08x_sg *dsg;
txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus); if (!txd->llis_va) {
dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__); return 0;
}
/* * Zero length is only allowed if all these requirements are * met: * - flow controller is peripheral. * - src.addr is aligned to src.width * - dst.addr is aligned to dst.width * * sg_len == 1 should be true, as there can be two cases here: * * - Memory addresses are contiguous and are not scattered. * Here, Only one sg will be passed by user driver, with * memory address and zero length. We pass this to controller * and after the transfer it will receive the last burst * request from peripheral and so transfer finishes. * * - Memory addresses are scattered and are not contiguous. * Here, Obviously as DMA controller doesn't know when a lli's * transfer gets over, it can't load next lli. So in this * case, there has to be an assumption that only one lli is * supported. Thus, we can't have scattered addresses.
*/ if (!bd.remainder) {
u32 fc;
/* FTDMAC020 only does memory-to-memory */ if (pl08x->vd->ftdmac020)
fc = PL080_FLOW_MEM2MEM; else
fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >>
PL080_CONFIG_FLOW_CONTROL_SHIFT; if (!((fc >= PL080_FLOW_SRC2DST_DST) &&
(fc <= PL080_FLOW_SRC2DST_SRC))) {
dev_err(&pl08x->adev->dev, "%s sg len can't be zero",
__func__); return 0;
}
if (!IS_BUS_ALIGNED(&bd.srcbus) ||
!IS_BUS_ALIGNED(&bd.dstbus)) {
dev_err(&pl08x->adev->dev, "%s src & dst address must be aligned to src" " & dst width if peripheral is flow controller",
__func__); return 0;
}
/* * Send byte by byte for following cases * - Less than a bus width available * - until master bus is aligned
*/ if (bd.remainder < mbus->buswidth)
early_bytes = bd.remainder; elseif (!IS_BUS_ALIGNED(mbus)) {
early_bytes = mbus->buswidth -
(mbus->addr & (mbus->buswidth - 1)); if ((bd.remainder - early_bytes) < mbus->buswidth)
early_bytes = bd.remainder;
}
if (bd.remainder) { /* * Master now aligned * - if slave is not then we must set its width down
*/ if (!IS_BUS_ALIGNED(sbus)) {
dev_dbg(&pl08x->adev->dev, "%s set down bus width to one byte\n",
__func__);
sbus->buswidth = 1;
}
/* * Bytes transferred = tsize * src width, not * MIN(buswidths)
*/
max_bytes_per_lli = bd.srcbus.buswidth *
pl08x->vd->max_transfer_size;
dev_vdbg(&pl08x->adev->dev, "%s max bytes per lli = %zu\n",
__func__, max_bytes_per_lli);
/* * Make largest possible LLIs until less than one bus * width left
*/ while (bd.remainder > (mbus->buswidth - 1)) {
size_t lli_len, tsize, width;
/* * If enough left try to send max possible, * otherwise try to send the remainder
*/
lli_len = min(bd.remainder, max_bytes_per_lli);
/* * Check against maximum bus alignment: * Calculate actual transfer size in relation to * bus width an get a maximum remainder of the * highest bus width - 1
*/
width = max(mbus->buswidth, sbus->buswidth);
lli_len = (lli_len / width) * width;
tsize = lli_len / bd.srcbus.buswidth;
dev_vdbg(&pl08x->adev->dev, "%s fill lli with single lli chunk of " "size 0x%08zx (remainder 0x%08zx)\n",
__func__, lli_len, bd.remainder);
/* * Send any odd bytes
*/ if (bd.remainder) {
dev_vdbg(&pl08x->adev->dev, "%s align with boundary, send odd bytes (remain %zu)\n",
__func__, bd.remainder);
prep_byte_width_lli(pl08x, &bd, &cctl,
bd.remainder, num_llis++, &total_bytes);
}
}
if (total_bytes != dsg->len) {
dev_err(&pl08x->adev->dev, "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
__func__, total_bytes, dsg->len); return 0;
}
if (num_llis >= MAX_NUM_TSFR_LLIS) {
dev_err(&pl08x->adev->dev, "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
__func__, MAX_NUM_TSFR_LLIS); return 0;
}
}
if (txd->cyclic) { /* Link back to the first LLI. */
last_lli[PL080_LLI_LLI] = txd->llis_bus | bd.lli_bus;
} else { /* The final LLI terminates the LLI. */
last_lli[PL080_LLI_LLI] = 0; /* The final LLI element shall also fire an interrupt. */ if (pl08x->vd->ftdmac020)
last_lli[PL080_LLI_CCTL] &= ~FTDMAC020_LLI_TC_MSK; else
last_lli[PL080_LLI_CCTL] |= PL080_CONTROL_TC_IRQ_EN;
}
/* * The DMA ENGINE API
*/ staticvoid pl08x_free_chan_resources(struct dma_chan *chan)
{ /* Ensure all queued descriptors are freed */
vchan_free_chan_resources(to_virt_chan(chan));
}
/* * Code accessing dma_async_is_complete() in a tight loop may give problems. * If slaves are relying on interrupts to signal completion this function * must not be called with interrupts disabled.
*/ staticenum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct virt_dma_desc *vd; unsignedlong flags; enum dma_status ret;
size_t bytes = 0;
ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_COMPLETE) return ret;
/* * There's no point calculating the residue if there's * no txstate to store the value.
*/ if (!txstate) { if (plchan->state == PL08X_CHAN_PAUSED)
ret = DMA_PAUSED; return ret;
}
spin_lock_irqsave(&plchan->vc.lock, flags);
ret = dma_cookie_status(chan, cookie, txstate); if (ret != DMA_COMPLETE) {
vd = vchan_find_desc(&plchan->vc, cookie); if (vd) { /* On the issued list, so hasn't been processed yet */ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx); struct pl08x_sg *dsg;
/* * Given the source and destination available bus masks, select which * will be routed to each port. We try to have source and destination * on separate ports, but always respect the allowable settings.
*/ static u32 pl08x_select_bus(bool ftdmac020, u8 src, u8 dst)
{
u32 cctl = 0;
u32 dst_ahb2;
u32 src_ahb2;
/* The FTDMAC020 use different bits to indicate src/dst bus */ if (ftdmac020) {
dst_ahb2 = FTDMAC020_LLI_DST_SEL;
src_ahb2 = FTDMAC020_LLI_SRC_SEL;
} else {
dst_ahb2 = PL080_CONTROL_DST_AHB2;
src_ahb2 = PL080_CONTROL_SRC_AHB2;
}
/* * If this channel will only request single transfers, set this * down to ONE element. Also select one element if no maxburst * is specified.
*/ if (plchan->cd->single)
maxburst = 1;
/* Conjure cctl */ switch (pl08x->pd->memcpy_bus_width) { default:
dev_err(&pl08x->adev->dev, "illegal bus width for memcpy, set to 8 bits\n");
fallthrough; case PL08X_BUS_WIDTH_8_BITS:
cctl |= PL080_WIDTH_8BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
PL080_WIDTH_8BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT; break; case PL08X_BUS_WIDTH_16_BITS:
cctl |= PL080_WIDTH_16BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
PL080_WIDTH_16BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT; break; case PL08X_BUS_WIDTH_32_BITS:
cctl |= PL080_WIDTH_32BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
PL080_WIDTH_32BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT; break;
}
/* * By default mask the TC IRQ on all LLIs, it will be unmasked on * the last LLI item by other code.
*/
cctl |= FTDMAC020_LLI_TC_MSK;
/* * Both to be incremented so leave bits FTDMAC020_LLI_SRCAD_CTL * and FTDMAC020_LLI_DSTAD_CTL as zero
*/ if (pl08x->vd->dualmaster)
cctl |= pl08x_select_bus(true,
pl08x->mem_buses,
pl08x->mem_buses);
return cctl;
}
/* * Initialize a descriptor to be used by memcpy submit
*/ staticstruct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
size_t len, unsignedlong flags)
{ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; struct pl08x_sg *dsg; int ret;
txd = pl08x_get_txd(plchan); if (!txd) {
dev_err(&pl08x->adev->dev, "%s no memory for descriptor\n", __func__); return NULL;
}
ret = pl08x_request_mux(plchan); if (ret < 0) {
pl08x_free_txd(pl08x, txd);
dev_dbg(&pl08x->adev->dev, "unable to mux for transfer on %s due to platform restrictions\n",
plchan->name); return NULL;
}
dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
plchan->signal, plchan->name);
/* Assign the flow control signal to this channel */ if (direction == DMA_MEM_TO_DEV)
txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT; else
txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
if (config->device_fc && pl08x->vd->pl080s) {
dev_err(&pl08x->adev->dev, "%s: PL080S does not support peripheral flow control\n",
__func__); return -EINVAL;
}
if (plchan->phychan) { /* * Mark physical channel as free and free any slave * signal
*/
pl08x_phy_free(plchan);
} /* Dequeue jobs and free LLIs */ if (plchan->at) {
vchan_terminate_vdesc(&plchan->at->vd);
plchan->at = NULL;
} /* Dequeue jobs not yet fired as well */
pl08x_free_txd_list(pl08x, plchan);
/* * Anything succeeds on channels with no physical allocation and * no queued transfers.
*/
spin_lock_irqsave(&plchan->vc.lock, flags); if (!plchan->phychan && !plchan->at) {
spin_unlock_irqrestore(&plchan->vc.lock, flags); return 0;
}
/* * Anything succeeds on channels with no physical allocation and * no queued transfers.
*/
spin_lock_irqsave(&plchan->vc.lock, flags); if (!plchan->phychan && !plchan->at) {
spin_unlock_irqrestore(&plchan->vc.lock, flags); return 0;
}
/* * Just check that the device is there and active * TODO: turn this bit on/off depending on the number of physical channels * actually used, if it is zero... well shut it off. That will save some * power. Cut the clock at the same time.
*/ staticvoid pl08x_ensure_on(struct pl08x_driver_data *pl08x)
{ /* The Nomadik variant does not have the config register */ if (pl08x->vd->nomadik) return; /* The FTDMAC020 variant does this in another register */ if (pl08x->vd->ftdmac020) {
writel(PL080_CONFIG_ENABLE, pl08x->base + FTDMAC020_CSR); return;
}
writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
}
for (i = 0; i < pl08x->vd->channels; i++) { if ((BIT(i) & err) || (BIT(i) & tc)) { /* Locate physical channel */ struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i]; struct pl08x_dma_chan *plchan = phychan->serving; struct pl08x_txd *tx;
if (!plchan) {
dev_err(&pl08x->adev->dev, "%s Error TC interrupt on unused channel: 0x%08x\n",
__func__, i); continue;
}
spin_lock(&plchan->vc.lock);
tx = plchan->at; if (tx && tx->cyclic) {
vchan_cyclic_callback(&tx->vd);
} elseif (tx) {
plchan->at = NULL; /* * This descriptor is done, release its mux * reservation.
*/
pl08x_release_mux(plchan);
tx->done = true;
vchan_cookie_complete(&tx->vd);
/* * And start the next descriptor (if any), * otherwise free this channel.
*/ if (vchan_next_desc(&plchan->vc))
pl08x_start_next_txd(plchan); else
pl08x_phy_free(plchan);
}
spin_unlock(&plchan->vc.lock);
/* * Initialise the DMAC memcpy/slave channels. * Make a local wrapper to hold required data
*/ staticint pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, struct dma_device *dmadev, unsignedint channels, bool slave)
{ struct pl08x_dma_chan *chan; int i;
INIT_LIST_HEAD(&dmadev->channels);
/* * Register as many memcpy as we have physical channels, * we won't always be able to use all but the code will have * to cope with that situation.
*/ for (i = 0; i < channels; i++) {
chan = kzalloc(sizeof(*chan), GFP_KERNEL); if (!chan) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.