162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2006 ARM Ltd.
462306a36Sopenharmony_ci * Copyright (c) 2010 ST-Ericsson SA
562306a36Sopenharmony_ci * Copyirght (c) 2017 Linaro Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Peter Pearse <peter.pearse@arm.com>
862306a36Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Documentation: ARM DDI 0196G == PL080
1162306a36Sopenharmony_ci * Documentation: ARM DDI 0218E == PL081
1262306a36Sopenharmony_ci * Documentation: S3C6410 User's Manual == PL080S
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * PL080 & PL081 both have 16 sets of DMA signals that can be routed to any
1562306a36Sopenharmony_ci * channel.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * The PL080 has 8 channels available for simultaneous use, and the PL081
1862306a36Sopenharmony_ci * has only two channels. So on these DMA controllers the number of channels
1962306a36Sopenharmony_ci * and the number of incoming DMA signals are two totally different things.
2062306a36Sopenharmony_ci * It is usually not possible to theoretically handle all physical signals,
2162306a36Sopenharmony_ci * so a multiplexing scheme with possible denial of use is necessary.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * The PL080 has a dual bus master, PL081 has a single master.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * PL080S is a version modified by Samsung and used in S3C64xx SoCs.
2662306a36Sopenharmony_ci * It differs in following aspects:
2762306a36Sopenharmony_ci * - CH_CONFIG register at different offset,
2862306a36Sopenharmony_ci * - separate CH_CONTROL2 register for transfer size,
2962306a36Sopenharmony_ci * - bigger maximum transfer size,
3062306a36Sopenharmony_ci * - 8-word aligned LLI, instead of 4-word, due to extra CCTL2 word,
3162306a36Sopenharmony_ci * - no support for peripheral flow control.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Memory to peripheral transfer may be visualized as
3462306a36Sopenharmony_ci *	Get data from memory to DMAC
3562306a36Sopenharmony_ci *	Until no data left
3662306a36Sopenharmony_ci *		On burst request from peripheral
3762306a36Sopenharmony_ci *			Destination burst from DMAC to peripheral
3862306a36Sopenharmony_ci *			Clear burst request
3962306a36Sopenharmony_ci *	Raise terminal count interrupt
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * For peripherals with a FIFO:
4262306a36Sopenharmony_ci * Source      burst size == half the depth of the peripheral FIFO
4362306a36Sopenharmony_ci * Destination burst size == the depth of the peripheral FIFO
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * (Bursts are irrelevant for mem to mem transfers - there are no burst
4662306a36Sopenharmony_ci * signals, the DMA controller will simply facilitate its AHB master.)
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * ASSUMES default (little) endianness for DMA transfers
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * The PL08x has two flow control settings:
5162306a36Sopenharmony_ci *  - DMAC flow control: the transfer size defines the number of transfers
5262306a36Sopenharmony_ci *    which occur for the current LLI entry, and the DMAC raises TC at the
5362306a36Sopenharmony_ci *    end of every LLI entry.  Observed behaviour shows the DMAC listening
5462306a36Sopenharmony_ci *    to both the BREQ and SREQ signals (contrary to documented),
5562306a36Sopenharmony_ci *    transferring data if either is active.  The LBREQ and LSREQ signals
5662306a36Sopenharmony_ci *    are ignored.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci *  - Peripheral flow control: the transfer size is ignored (and should be
5962306a36Sopenharmony_ci *    zero).  The data is transferred from the current LLI entry, until
6062306a36Sopenharmony_ci *    after the final transfer signalled by LBREQ or LSREQ.  The DMAC
6162306a36Sopenharmony_ci *    will then move to the next LLI entry. Unsupported by PL080S.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ci#include <linux/amba/bus.h>
6462306a36Sopenharmony_ci#include <linux/amba/pl08x.h>
6562306a36Sopenharmony_ci#include <linux/debugfs.h>
6662306a36Sopenharmony_ci#include <linux/delay.h>
6762306a36Sopenharmony_ci#include <linux/device.h>
6862306a36Sopenharmony_ci#include <linux/dmaengine.h>
6962306a36Sopenharmony_ci#include <linux/dmapool.h>
7062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
7162306a36Sopenharmony_ci#include <linux/export.h>
7262306a36Sopenharmony_ci#include <linux/init.h>
7362306a36Sopenharmony_ci#include <linux/interrupt.h>
7462306a36Sopenharmony_ci#include <linux/module.h>
7562306a36Sopenharmony_ci#include <linux/of.h>
7662306a36Sopenharmony_ci#include <linux/of_dma.h>
7762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
7862306a36Sopenharmony_ci#include <linux/seq_file.h>
7962306a36Sopenharmony_ci#include <linux/slab.h>
8062306a36Sopenharmony_ci#include <linux/amba/pl080.h>
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#include "dmaengine.h"
8362306a36Sopenharmony_ci#include "virt-dma.h"
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define DRIVER_NAME	"pl08xdmac"
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define PL80X_DMA_BUSWIDTHS \
8862306a36Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \
8962306a36Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
9062306a36Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
9162306a36Sopenharmony_ci	BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct amba_driver pl08x_amba_driver;
9462306a36Sopenharmony_cistruct pl08x_driver_data;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/**
9762306a36Sopenharmony_ci * struct vendor_data - vendor-specific config parameters for PL08x derivatives
9862306a36Sopenharmony_ci * @config_offset: offset to the configuration register
9962306a36Sopenharmony_ci * @channels: the number of channels available in this variant
10062306a36Sopenharmony_ci * @signals: the number of request signals available from the hardware
10162306a36Sopenharmony_ci * @dualmaster: whether this version supports dual AHB masters or not.
10262306a36Sopenharmony_ci * @nomadik: whether this variant is a ST Microelectronics Nomadik, where the
10362306a36Sopenharmony_ci *	channels have Nomadik security extension bits that need to be checked
10462306a36Sopenharmony_ci *	for permission before use and some registers are missing
10562306a36Sopenharmony_ci * @pl080s: whether this variant is a Samsung PL080S, which has separate
10662306a36Sopenharmony_ci *	register and LLI word for transfer size.
10762306a36Sopenharmony_ci * @ftdmac020: whether this variant is a Faraday Technology FTDMAC020
10862306a36Sopenharmony_ci * @max_transfer_size: the maximum single element transfer size for this
10962306a36Sopenharmony_ci *	PL08x variant.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistruct vendor_data {
11262306a36Sopenharmony_ci	u8 config_offset;
11362306a36Sopenharmony_ci	u8 channels;
11462306a36Sopenharmony_ci	u8 signals;
11562306a36Sopenharmony_ci	bool dualmaster;
11662306a36Sopenharmony_ci	bool nomadik;
11762306a36Sopenharmony_ci	bool pl080s;
11862306a36Sopenharmony_ci	bool ftdmac020;
11962306a36Sopenharmony_ci	u32 max_transfer_size;
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/**
12362306a36Sopenharmony_ci * struct pl08x_bus_data - information of source or destination
12462306a36Sopenharmony_ci * busses for a transfer
12562306a36Sopenharmony_ci * @addr: current address
12662306a36Sopenharmony_ci * @maxwidth: the maximum width of a transfer on this bus
12762306a36Sopenharmony_ci * @buswidth: the width of this bus in bytes: 1, 2 or 4
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistruct pl08x_bus_data {
13062306a36Sopenharmony_ci	dma_addr_t addr;
13162306a36Sopenharmony_ci	u8 maxwidth;
13262306a36Sopenharmony_ci	u8 buswidth;
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define IS_BUS_ALIGNED(bus) IS_ALIGNED((bus)->addr, (bus)->buswidth)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/**
13862306a36Sopenharmony_ci * struct pl08x_phy_chan - holder for the physical channels
13962306a36Sopenharmony_ci * @id: physical index to this channel
14062306a36Sopenharmony_ci * @base: memory base address for this physical channel
14162306a36Sopenharmony_ci * @reg_config: configuration address for this physical channel
14262306a36Sopenharmony_ci * @reg_control: control address for this physical channel
14362306a36Sopenharmony_ci * @reg_src: transfer source address register
14462306a36Sopenharmony_ci * @reg_dst: transfer destination address register
14562306a36Sopenharmony_ci * @reg_lli: transfer LLI address register
14662306a36Sopenharmony_ci * @reg_busy: if the variant has a special per-channel busy register,
14762306a36Sopenharmony_ci * this contains a pointer to it
14862306a36Sopenharmony_ci * @lock: a lock to use when altering an instance of this struct
14962306a36Sopenharmony_ci * @serving: the virtual channel currently being served by this physical
15062306a36Sopenharmony_ci * channel
15162306a36Sopenharmony_ci * @locked: channel unavailable for the system, e.g. dedicated to secure
15262306a36Sopenharmony_ci * world
15362306a36Sopenharmony_ci * @ftdmac020: channel is on a FTDMAC020
15462306a36Sopenharmony_ci * @pl080s: channel is on a PL08s
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistruct pl08x_phy_chan {
15762306a36Sopenharmony_ci	unsigned int id;
15862306a36Sopenharmony_ci	void __iomem *base;
15962306a36Sopenharmony_ci	void __iomem *reg_config;
16062306a36Sopenharmony_ci	void __iomem *reg_control;
16162306a36Sopenharmony_ci	void __iomem *reg_src;
16262306a36Sopenharmony_ci	void __iomem *reg_dst;
16362306a36Sopenharmony_ci	void __iomem *reg_lli;
16462306a36Sopenharmony_ci	void __iomem *reg_busy;
16562306a36Sopenharmony_ci	spinlock_t lock;
16662306a36Sopenharmony_ci	struct pl08x_dma_chan *serving;
16762306a36Sopenharmony_ci	bool locked;
16862306a36Sopenharmony_ci	bool ftdmac020;
16962306a36Sopenharmony_ci	bool pl080s;
17062306a36Sopenharmony_ci};
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/**
17362306a36Sopenharmony_ci * struct pl08x_sg - structure containing data per sg
17462306a36Sopenharmony_ci * @src_addr: src address of sg
17562306a36Sopenharmony_ci * @dst_addr: dst address of sg
17662306a36Sopenharmony_ci * @len: transfer len in bytes
17762306a36Sopenharmony_ci * @node: node for txd's dsg_list
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistruct pl08x_sg {
18062306a36Sopenharmony_ci	dma_addr_t src_addr;
18162306a36Sopenharmony_ci	dma_addr_t dst_addr;
18262306a36Sopenharmony_ci	size_t len;
18362306a36Sopenharmony_ci	struct list_head node;
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
18862306a36Sopenharmony_ci * @vd: virtual DMA descriptor
18962306a36Sopenharmony_ci * @dsg_list: list of children sg's
19062306a36Sopenharmony_ci * @llis_bus: DMA memory address (physical) start for the LLIs
19162306a36Sopenharmony_ci * @llis_va: virtual memory address start for the LLIs
19262306a36Sopenharmony_ci * @cctl: control reg values for current txd
19362306a36Sopenharmony_ci * @ccfg: config reg values for current txd
19462306a36Sopenharmony_ci * @done: this marks completed descriptors, which should not have their
19562306a36Sopenharmony_ci *   mux released.
19662306a36Sopenharmony_ci * @cyclic: indicate cyclic transfers
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistruct pl08x_txd {
19962306a36Sopenharmony_ci	struct virt_dma_desc vd;
20062306a36Sopenharmony_ci	struct list_head dsg_list;
20162306a36Sopenharmony_ci	dma_addr_t llis_bus;
20262306a36Sopenharmony_ci	u32 *llis_va;
20362306a36Sopenharmony_ci	/* Default cctl value for LLIs */
20462306a36Sopenharmony_ci	u32 cctl;
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * Settings to be put into the physical channel when we
20762306a36Sopenharmony_ci	 * trigger this txd.  Other registers are in llis_va[0].
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	u32 ccfg;
21062306a36Sopenharmony_ci	bool done;
21162306a36Sopenharmony_ci	bool cyclic;
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/**
21562306a36Sopenharmony_ci * enum pl08x_dma_chan_state - holds the PL08x specific virtual channel
21662306a36Sopenharmony_ci * states
21762306a36Sopenharmony_ci * @PL08X_CHAN_IDLE: the channel is idle
21862306a36Sopenharmony_ci * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
21962306a36Sopenharmony_ci * channel and is running a transfer on it
22062306a36Sopenharmony_ci * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
22162306a36Sopenharmony_ci * channel, but the transfer is currently paused
22262306a36Sopenharmony_ci * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
22362306a36Sopenharmony_ci * channel to become available (only pertains to memcpy channels)
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cienum pl08x_dma_chan_state {
22662306a36Sopenharmony_ci	PL08X_CHAN_IDLE,
22762306a36Sopenharmony_ci	PL08X_CHAN_RUNNING,
22862306a36Sopenharmony_ci	PL08X_CHAN_PAUSED,
22962306a36Sopenharmony_ci	PL08X_CHAN_WAITING,
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/**
23362306a36Sopenharmony_ci * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
23462306a36Sopenharmony_ci * @vc: wrapped virtual channel
23562306a36Sopenharmony_ci * @phychan: the physical channel utilized by this channel, if there is one
23662306a36Sopenharmony_ci * @name: name of channel
23762306a36Sopenharmony_ci * @cd: channel platform data
23862306a36Sopenharmony_ci * @cfg: slave configuration
23962306a36Sopenharmony_ci * @at: active transaction on this channel
24062306a36Sopenharmony_ci * @host: a pointer to the host (internal use)
24162306a36Sopenharmony_ci * @state: whether the channel is idle, paused, running etc
24262306a36Sopenharmony_ci * @slave: whether this channel is a device (slave) or for memcpy
24362306a36Sopenharmony_ci * @signal: the physical DMA request signal which this channel is using
24462306a36Sopenharmony_ci * @mux_use: count of descriptors using this DMA request signal setting
24562306a36Sopenharmony_ci * @waiting_at: time in jiffies when this channel moved to waiting state
24662306a36Sopenharmony_ci */
24762306a36Sopenharmony_cistruct pl08x_dma_chan {
24862306a36Sopenharmony_ci	struct virt_dma_chan vc;
24962306a36Sopenharmony_ci	struct pl08x_phy_chan *phychan;
25062306a36Sopenharmony_ci	const char *name;
25162306a36Sopenharmony_ci	struct pl08x_channel_data *cd;
25262306a36Sopenharmony_ci	struct dma_slave_config cfg;
25362306a36Sopenharmony_ci	struct pl08x_txd *at;
25462306a36Sopenharmony_ci	struct pl08x_driver_data *host;
25562306a36Sopenharmony_ci	enum pl08x_dma_chan_state state;
25662306a36Sopenharmony_ci	bool slave;
25762306a36Sopenharmony_ci	int signal;
25862306a36Sopenharmony_ci	unsigned mux_use;
25962306a36Sopenharmony_ci	unsigned long waiting_at;
26062306a36Sopenharmony_ci};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/**
26362306a36Sopenharmony_ci * struct pl08x_driver_data - the local state holder for the PL08x
26462306a36Sopenharmony_ci * @slave: optional slave engine for this instance
26562306a36Sopenharmony_ci * @memcpy: memcpy engine for this instance
26662306a36Sopenharmony_ci * @has_slave: the PL08x has a slave engine (routed signals)
26762306a36Sopenharmony_ci * @base: virtual memory base (remapped) for the PL08x
26862306a36Sopenharmony_ci * @adev: the corresponding AMBA (PrimeCell) bus entry
26962306a36Sopenharmony_ci * @vd: vendor data for this PL08x variant
27062306a36Sopenharmony_ci * @pd: platform data passed in from the platform/machine
27162306a36Sopenharmony_ci * @phy_chans: array of data for the physical channels
27262306a36Sopenharmony_ci * @pool: a pool for the LLI descriptors
27362306a36Sopenharmony_ci * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
27462306a36Sopenharmony_ci * fetches
27562306a36Sopenharmony_ci * @mem_buses: set to indicate memory transfers on AHB2.
27662306a36Sopenharmony_ci * @lli_words: how many words are used in each LLI item for this variant
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_cistruct pl08x_driver_data {
27962306a36Sopenharmony_ci	struct dma_device slave;
28062306a36Sopenharmony_ci	struct dma_device memcpy;
28162306a36Sopenharmony_ci	bool has_slave;
28262306a36Sopenharmony_ci	void __iomem *base;
28362306a36Sopenharmony_ci	struct amba_device *adev;
28462306a36Sopenharmony_ci	const struct vendor_data *vd;
28562306a36Sopenharmony_ci	struct pl08x_platform_data *pd;
28662306a36Sopenharmony_ci	struct pl08x_phy_chan *phy_chans;
28762306a36Sopenharmony_ci	struct dma_pool *pool;
28862306a36Sopenharmony_ci	u8 lli_buses;
28962306a36Sopenharmony_ci	u8 mem_buses;
29062306a36Sopenharmony_ci	u8 lli_words;
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * PL08X specific defines
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* The order of words in an LLI. */
29862306a36Sopenharmony_ci#define PL080_LLI_SRC		0
29962306a36Sopenharmony_ci#define PL080_LLI_DST		1
30062306a36Sopenharmony_ci#define PL080_LLI_LLI		2
30162306a36Sopenharmony_ci#define PL080_LLI_CCTL		3
30262306a36Sopenharmony_ci#define PL080S_LLI_CCTL2	4
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/* Total words in an LLI. */
30562306a36Sopenharmony_ci#define PL080_LLI_WORDS		4
30662306a36Sopenharmony_ci#define PL080S_LLI_WORDS	8
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/*
30962306a36Sopenharmony_ci * Number of LLIs in each LLI buffer allocated for one transfer
31062306a36Sopenharmony_ci * (maximum times we call dma_pool_alloc on this pool without freeing)
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ci#define MAX_NUM_TSFR_LLIS	512
31362306a36Sopenharmony_ci#define PL08X_ALIGN		8
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	return container_of(chan, struct pl08x_dma_chan, vc.chan);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return container_of(tx, struct pl08x_txd, vd.tx);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Mux handling.
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * This gives us the DMA request input to the PL08x primecell which the
32962306a36Sopenharmony_ci * peripheral described by the channel data will be routed to, possibly
33062306a36Sopenharmony_ci * via a board/SoC specific external MUX.  One important point to note
33162306a36Sopenharmony_ci * here is that this does not depend on the physical channel.
33262306a36Sopenharmony_ci */
33362306a36Sopenharmony_cistatic int pl08x_request_mux(struct pl08x_dma_chan *plchan)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	const struct pl08x_platform_data *pd = plchan->host->pd;
33662306a36Sopenharmony_ci	int ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (plchan->mux_use++ == 0 && pd->get_xfer_signal) {
33962306a36Sopenharmony_ci		ret = pd->get_xfer_signal(plchan->cd);
34062306a36Sopenharmony_ci		if (ret < 0) {
34162306a36Sopenharmony_ci			plchan->mux_use = 0;
34262306a36Sopenharmony_ci			return ret;
34362306a36Sopenharmony_ci		}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		plchan->signal = ret;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void pl08x_release_mux(struct pl08x_dma_chan *plchan)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	const struct pl08x_platform_data *pd = plchan->host->pd;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (plchan->signal >= 0) {
35562306a36Sopenharmony_ci		WARN_ON(plchan->mux_use == 0);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci		if (--plchan->mux_use == 0 && pd->put_xfer_signal) {
35862306a36Sopenharmony_ci			pd->put_xfer_signal(plchan->cd, plchan->signal);
35962306a36Sopenharmony_ci			plchan->signal = -1;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/*
36562306a36Sopenharmony_ci * Physical channel handling
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* Whether a certain channel is busy or not */
36962306a36Sopenharmony_cistatic int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	unsigned int val;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/* If we have a special busy register, take a shortcut */
37462306a36Sopenharmony_ci	if (ch->reg_busy) {
37562306a36Sopenharmony_ci		val = readl(ch->reg_busy);
37662306a36Sopenharmony_ci		return !!(val & BIT(ch->id));
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	val = readl(ch->reg_config);
37962306a36Sopenharmony_ci	return val & PL080_CONFIG_ACTIVE;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/*
38362306a36Sopenharmony_ci * pl08x_write_lli() - Write an LLI into the DMA controller.
38462306a36Sopenharmony_ci *
38562306a36Sopenharmony_ci * The PL08x derivatives support linked lists, but the first item of the
38662306a36Sopenharmony_ci * list containing the source, destination, control word and next LLI is
38762306a36Sopenharmony_ci * ignored. Instead the driver has to write those values directly into the
38862306a36Sopenharmony_ci * SRC, DST, LLI and control registers. On FTDMAC020 also the SIZE
38962306a36Sopenharmony_ci * register need to be set up for the first transfer.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic void pl08x_write_lli(struct pl08x_driver_data *pl08x,
39262306a36Sopenharmony_ci		struct pl08x_phy_chan *phychan, const u32 *lli, u32 ccfg)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	if (pl08x->vd->pl080s)
39562306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev,
39662306a36Sopenharmony_ci			"WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
39762306a36Sopenharmony_ci			"clli=0x%08x, cctl=0x%08x, cctl2=0x%08x, ccfg=0x%08x\n",
39862306a36Sopenharmony_ci			phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST],
39962306a36Sopenharmony_ci			lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL],
40062306a36Sopenharmony_ci			lli[PL080S_LLI_CCTL2], ccfg);
40162306a36Sopenharmony_ci	else
40262306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev,
40362306a36Sopenharmony_ci			"WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
40462306a36Sopenharmony_ci			"clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n",
40562306a36Sopenharmony_ci			phychan->id, lli[PL080_LLI_SRC], lli[PL080_LLI_DST],
40662306a36Sopenharmony_ci			lli[PL080_LLI_LLI], lli[PL080_LLI_CCTL], ccfg);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	writel_relaxed(lli[PL080_LLI_SRC], phychan->reg_src);
40962306a36Sopenharmony_ci	writel_relaxed(lli[PL080_LLI_DST], phychan->reg_dst);
41062306a36Sopenharmony_ci	writel_relaxed(lli[PL080_LLI_LLI], phychan->reg_lli);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/*
41362306a36Sopenharmony_ci	 * The FTMAC020 has a different layout in the CCTL word of the LLI
41462306a36Sopenharmony_ci	 * and the CCTL register which is split in CSR and SIZE registers.
41562306a36Sopenharmony_ci	 * Convert the LLI item CCTL into the proper values to write into
41662306a36Sopenharmony_ci	 * the CSR and SIZE registers.
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	if (phychan->ftdmac020) {
41962306a36Sopenharmony_ci		u32 llictl = lli[PL080_LLI_CCTL];
42062306a36Sopenharmony_ci		u32 val = 0;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		/* Write the transfer size (12 bits) to the size register */
42362306a36Sopenharmony_ci		writel_relaxed(llictl & FTDMAC020_LLI_TRANSFER_SIZE_MASK,
42462306a36Sopenharmony_ci			       phychan->base + FTDMAC020_CH_SIZE);
42562306a36Sopenharmony_ci		/*
42662306a36Sopenharmony_ci		 * Then write the control bits 28..16 to the control register
42762306a36Sopenharmony_ci		 * by shuffleing the bits around to where they are in the
42862306a36Sopenharmony_ci		 * main register. The mapping is as follows:
42962306a36Sopenharmony_ci		 * Bit 28: TC_MSK - mask on all except last LLI
43062306a36Sopenharmony_ci		 * Bit 27..25: SRC_WIDTH
43162306a36Sopenharmony_ci		 * Bit 24..22: DST_WIDTH
43262306a36Sopenharmony_ci		 * Bit 21..20: SRCAD_CTRL
43362306a36Sopenharmony_ci		 * Bit 19..17: DSTAD_CTRL
43462306a36Sopenharmony_ci		 * Bit 17: SRC_SEL
43562306a36Sopenharmony_ci		 * Bit 16: DST_SEL
43662306a36Sopenharmony_ci		 */
43762306a36Sopenharmony_ci		if (llictl & FTDMAC020_LLI_TC_MSK)
43862306a36Sopenharmony_ci			val |= FTDMAC020_CH_CSR_TC_MSK;
43962306a36Sopenharmony_ci		val |= ((llictl  & FTDMAC020_LLI_SRC_WIDTH_MSK) >>
44062306a36Sopenharmony_ci			(FTDMAC020_LLI_SRC_WIDTH_SHIFT -
44162306a36Sopenharmony_ci			 FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT));
44262306a36Sopenharmony_ci		val |= ((llictl  & FTDMAC020_LLI_DST_WIDTH_MSK) >>
44362306a36Sopenharmony_ci			(FTDMAC020_LLI_DST_WIDTH_SHIFT -
44462306a36Sopenharmony_ci			 FTDMAC020_CH_CSR_DST_WIDTH_SHIFT));
44562306a36Sopenharmony_ci		val |= ((llictl  & FTDMAC020_LLI_SRCAD_CTL_MSK) >>
44662306a36Sopenharmony_ci			(FTDMAC020_LLI_SRCAD_CTL_SHIFT -
44762306a36Sopenharmony_ci			 FTDMAC020_CH_CSR_SRCAD_CTL_SHIFT));
44862306a36Sopenharmony_ci		val |= ((llictl  & FTDMAC020_LLI_DSTAD_CTL_MSK) >>
44962306a36Sopenharmony_ci			(FTDMAC020_LLI_DSTAD_CTL_SHIFT -
45062306a36Sopenharmony_ci			 FTDMAC020_CH_CSR_DSTAD_CTL_SHIFT));
45162306a36Sopenharmony_ci		if (llictl & FTDMAC020_LLI_SRC_SEL)
45262306a36Sopenharmony_ci			val |= FTDMAC020_CH_CSR_SRC_SEL;
45362306a36Sopenharmony_ci		if (llictl & FTDMAC020_LLI_DST_SEL)
45462306a36Sopenharmony_ci			val |= FTDMAC020_CH_CSR_DST_SEL;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		/*
45762306a36Sopenharmony_ci		 * Set up the bits that exist in the CSR but are not
45862306a36Sopenharmony_ci		 * part the LLI, i.e. only gets written to the control
45962306a36Sopenharmony_ci		 * register right here.
46062306a36Sopenharmony_ci		 *
46162306a36Sopenharmony_ci		 * FIXME: do not just handle memcpy, also handle slave DMA.
46262306a36Sopenharmony_ci		 */
46362306a36Sopenharmony_ci		switch (pl08x->pd->memcpy_burst_size) {
46462306a36Sopenharmony_ci		default:
46562306a36Sopenharmony_ci		case PL08X_BURST_SZ_1:
46662306a36Sopenharmony_ci			val |= PL080_BSIZE_1 <<
46762306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
46862306a36Sopenharmony_ci			break;
46962306a36Sopenharmony_ci		case PL08X_BURST_SZ_4:
47062306a36Sopenharmony_ci			val |= PL080_BSIZE_4 <<
47162306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
47262306a36Sopenharmony_ci			break;
47362306a36Sopenharmony_ci		case PL08X_BURST_SZ_8:
47462306a36Sopenharmony_ci			val |= PL080_BSIZE_8 <<
47562306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
47662306a36Sopenharmony_ci			break;
47762306a36Sopenharmony_ci		case PL08X_BURST_SZ_16:
47862306a36Sopenharmony_ci			val |= PL080_BSIZE_16 <<
47962306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
48062306a36Sopenharmony_ci			break;
48162306a36Sopenharmony_ci		case PL08X_BURST_SZ_32:
48262306a36Sopenharmony_ci			val |= PL080_BSIZE_32 <<
48362306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
48462306a36Sopenharmony_ci			break;
48562306a36Sopenharmony_ci		case PL08X_BURST_SZ_64:
48662306a36Sopenharmony_ci			val |= PL080_BSIZE_64 <<
48762306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
48862306a36Sopenharmony_ci			break;
48962306a36Sopenharmony_ci		case PL08X_BURST_SZ_128:
49062306a36Sopenharmony_ci			val |= PL080_BSIZE_128 <<
49162306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
49262306a36Sopenharmony_ci			break;
49362306a36Sopenharmony_ci		case PL08X_BURST_SZ_256:
49462306a36Sopenharmony_ci			val |= PL080_BSIZE_256 <<
49562306a36Sopenharmony_ci				FTDMAC020_CH_CSR_SRC_SIZE_SHIFT;
49662306a36Sopenharmony_ci			break;
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		/* Protection flags */
50062306a36Sopenharmony_ci		if (pl08x->pd->memcpy_prot_buff)
50162306a36Sopenharmony_ci			val |= FTDMAC020_CH_CSR_PROT2;
50262306a36Sopenharmony_ci		if (pl08x->pd->memcpy_prot_cache)
50362306a36Sopenharmony_ci			val |= FTDMAC020_CH_CSR_PROT3;
50462306a36Sopenharmony_ci		/* We are the kernel, so we are in privileged mode */
50562306a36Sopenharmony_ci		val |= FTDMAC020_CH_CSR_PROT1;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		writel_relaxed(val, phychan->reg_control);
50862306a36Sopenharmony_ci	} else {
50962306a36Sopenharmony_ci		/* Bits are just identical */
51062306a36Sopenharmony_ci		writel_relaxed(lli[PL080_LLI_CCTL], phychan->reg_control);
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	/* Second control word on the PL080s */
51462306a36Sopenharmony_ci	if (pl08x->vd->pl080s)
51562306a36Sopenharmony_ci		writel_relaxed(lli[PL080S_LLI_CCTL2],
51662306a36Sopenharmony_ci				phychan->base + PL080S_CH_CONTROL2);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	writel(ccfg, phychan->reg_config);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/*
52262306a36Sopenharmony_ci * Set the initial DMA register values i.e. those for the first LLI
52362306a36Sopenharmony_ci * The next LLI pointer and the configuration interrupt bit have
52462306a36Sopenharmony_ci * been set when the LLIs were constructed.  Poke them into the hardware
52562306a36Sopenharmony_ci * and start the transfer.
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_cistatic void pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
53062306a36Sopenharmony_ci	struct pl08x_phy_chan *phychan = plchan->phychan;
53162306a36Sopenharmony_ci	struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc);
53262306a36Sopenharmony_ci	struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
53362306a36Sopenharmony_ci	u32 val;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	list_del(&txd->vd.node);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	plchan->at = txd;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* Wait for channel inactive */
54062306a36Sopenharmony_ci	while (pl08x_phy_channel_busy(phychan))
54162306a36Sopenharmony_ci		cpu_relax();
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pl08x_write_lli(pl08x, phychan, &txd->llis_va[0], txd->ccfg);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* Enable the DMA channel */
54662306a36Sopenharmony_ci	/* Do not access config register until channel shows as disabled */
54762306a36Sopenharmony_ci	while (readl(pl08x->base + PL080_EN_CHAN) & BIT(phychan->id))
54862306a36Sopenharmony_ci		cpu_relax();
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Do not access config register until channel shows as inactive */
55162306a36Sopenharmony_ci	if (phychan->ftdmac020) {
55262306a36Sopenharmony_ci		val = readl(phychan->reg_config);
55362306a36Sopenharmony_ci		while (val & FTDMAC020_CH_CFG_BUSY)
55462306a36Sopenharmony_ci			val = readl(phychan->reg_config);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		val = readl(phychan->reg_control);
55762306a36Sopenharmony_ci		while (val & FTDMAC020_CH_CSR_EN)
55862306a36Sopenharmony_ci			val = readl(phychan->reg_control);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		writel(val | FTDMAC020_CH_CSR_EN,
56162306a36Sopenharmony_ci		       phychan->reg_control);
56262306a36Sopenharmony_ci	} else {
56362306a36Sopenharmony_ci		val = readl(phychan->reg_config);
56462306a36Sopenharmony_ci		while ((val & PL080_CONFIG_ACTIVE) ||
56562306a36Sopenharmony_ci		       (val & PL080_CONFIG_ENABLE))
56662306a36Sopenharmony_ci			val = readl(phychan->reg_config);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		writel(val | PL080_CONFIG_ENABLE, phychan->reg_config);
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/*
57362306a36Sopenharmony_ci * Pause the channel by setting the HALT bit.
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * For M->P transfers, pause the DMAC first and then stop the peripheral -
57662306a36Sopenharmony_ci * the FIFO can only drain if the peripheral is still requesting data.
57762306a36Sopenharmony_ci * (note: this can still timeout if the DMAC FIFO never drains of data.)
57862306a36Sopenharmony_ci *
57962306a36Sopenharmony_ci * For P->M transfers, disable the peripheral first to stop it filling
58062306a36Sopenharmony_ci * the DMAC FIFO, and then pause the DMAC.
58162306a36Sopenharmony_ci */
58262306a36Sopenharmony_cistatic void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	u32 val;
58562306a36Sopenharmony_ci	int timeout;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (ch->ftdmac020) {
58862306a36Sopenharmony_ci		/* Use the enable bit on the FTDMAC020 */
58962306a36Sopenharmony_ci		val = readl(ch->reg_control);
59062306a36Sopenharmony_ci		val &= ~FTDMAC020_CH_CSR_EN;
59162306a36Sopenharmony_ci		writel(val, ch->reg_control);
59262306a36Sopenharmony_ci		return;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* Set the HALT bit and wait for the FIFO to drain */
59662306a36Sopenharmony_ci	val = readl(ch->reg_config);
59762306a36Sopenharmony_ci	val |= PL080_CONFIG_HALT;
59862306a36Sopenharmony_ci	writel(val, ch->reg_config);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Wait for channel inactive */
60162306a36Sopenharmony_ci	for (timeout = 1000; timeout; timeout--) {
60262306a36Sopenharmony_ci		if (!pl08x_phy_channel_busy(ch))
60362306a36Sopenharmony_ci			break;
60462306a36Sopenharmony_ci		udelay(1);
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci	if (pl08x_phy_channel_busy(ch))
60762306a36Sopenharmony_ci		pr_err("pl08x: channel%u timeout waiting for pause\n", ch->id);
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	u32 val;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Use the enable bit on the FTDMAC020 */
61562306a36Sopenharmony_ci	if (ch->ftdmac020) {
61662306a36Sopenharmony_ci		val = readl(ch->reg_control);
61762306a36Sopenharmony_ci		val |= FTDMAC020_CH_CSR_EN;
61862306a36Sopenharmony_ci		writel(val, ch->reg_control);
61962306a36Sopenharmony_ci		return;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* Clear the HALT bit */
62362306a36Sopenharmony_ci	val = readl(ch->reg_config);
62462306a36Sopenharmony_ci	val &= ~PL080_CONFIG_HALT;
62562306a36Sopenharmony_ci	writel(val, ch->reg_config);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci/*
62962306a36Sopenharmony_ci * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and
63062306a36Sopenharmony_ci * clears any pending interrupt status.  This should not be used for
63162306a36Sopenharmony_ci * an on-going transfer, but as a method of shutting down a channel
63262306a36Sopenharmony_ci * (eg, when it's no longer used) or terminating a transfer.
63362306a36Sopenharmony_ci */
63462306a36Sopenharmony_cistatic void pl08x_terminate_phy_chan(struct pl08x_driver_data *pl08x,
63562306a36Sopenharmony_ci	struct pl08x_phy_chan *ch)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	u32 val;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* The layout for the FTDMAC020 is different */
64062306a36Sopenharmony_ci	if (ch->ftdmac020) {
64162306a36Sopenharmony_ci		/* Disable all interrupts */
64262306a36Sopenharmony_ci		val = readl(ch->reg_config);
64362306a36Sopenharmony_ci		val |= (FTDMAC020_CH_CFG_INT_ABT_MASK |
64462306a36Sopenharmony_ci			FTDMAC020_CH_CFG_INT_ERR_MASK |
64562306a36Sopenharmony_ci			FTDMAC020_CH_CFG_INT_TC_MASK);
64662306a36Sopenharmony_ci		writel(val, ch->reg_config);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		/* Abort and disable channel */
64962306a36Sopenharmony_ci		val = readl(ch->reg_control);
65062306a36Sopenharmony_ci		val &= ~FTDMAC020_CH_CSR_EN;
65162306a36Sopenharmony_ci		val |= FTDMAC020_CH_CSR_ABT;
65262306a36Sopenharmony_ci		writel(val, ch->reg_control);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		/* Clear ABT and ERR interrupt flags */
65562306a36Sopenharmony_ci		writel(BIT(ch->id) | BIT(ch->id + 16),
65662306a36Sopenharmony_ci		       pl08x->base + PL080_ERR_CLEAR);
65762306a36Sopenharmony_ci		writel(BIT(ch->id), pl08x->base + PL080_TC_CLEAR);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		return;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	val = readl(ch->reg_config);
66362306a36Sopenharmony_ci	val &= ~(PL080_CONFIG_ENABLE | PL080_CONFIG_ERR_IRQ_MASK |
66462306a36Sopenharmony_ci		 PL080_CONFIG_TC_IRQ_MASK);
66562306a36Sopenharmony_ci	writel(val, ch->reg_config);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	writel(BIT(ch->id), pl08x->base + PL080_ERR_CLEAR);
66862306a36Sopenharmony_ci	writel(BIT(ch->id), pl08x->base + PL080_TC_CLEAR);
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic u32 get_bytes_in_phy_channel(struct pl08x_phy_chan *ch)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	u32 val;
67462306a36Sopenharmony_ci	u32 bytes;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	if (ch->ftdmac020) {
67762306a36Sopenharmony_ci		bytes = readl(ch->base + FTDMAC020_CH_SIZE);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		val = readl(ch->reg_control);
68062306a36Sopenharmony_ci		val &= FTDMAC020_CH_CSR_SRC_WIDTH_MSK;
68162306a36Sopenharmony_ci		val >>= FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT;
68262306a36Sopenharmony_ci	} else if (ch->pl080s) {
68362306a36Sopenharmony_ci		val = readl(ch->base + PL080S_CH_CONTROL2);
68462306a36Sopenharmony_ci		bytes = val & PL080S_CONTROL_TRANSFER_SIZE_MASK;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		val = readl(ch->reg_control);
68762306a36Sopenharmony_ci		val &= PL080_CONTROL_SWIDTH_MASK;
68862306a36Sopenharmony_ci		val >>= PL080_CONTROL_SWIDTH_SHIFT;
68962306a36Sopenharmony_ci	} else {
69062306a36Sopenharmony_ci		/* Plain PL08x */
69162306a36Sopenharmony_ci		val = readl(ch->reg_control);
69262306a36Sopenharmony_ci		bytes = val & PL080_CONTROL_TRANSFER_SIZE_MASK;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		val &= PL080_CONTROL_SWIDTH_MASK;
69562306a36Sopenharmony_ci		val >>= PL080_CONTROL_SWIDTH_SHIFT;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	switch (val) {
69962306a36Sopenharmony_ci	case PL080_WIDTH_8BIT:
70062306a36Sopenharmony_ci		break;
70162306a36Sopenharmony_ci	case PL080_WIDTH_16BIT:
70262306a36Sopenharmony_ci		bytes *= 2;
70362306a36Sopenharmony_ci		break;
70462306a36Sopenharmony_ci	case PL080_WIDTH_32BIT:
70562306a36Sopenharmony_ci		bytes *= 4;
70662306a36Sopenharmony_ci		break;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci	return bytes;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic u32 get_bytes_in_lli(struct pl08x_phy_chan *ch, const u32 *llis_va)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	u32 val;
71462306a36Sopenharmony_ci	u32 bytes;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (ch->ftdmac020) {
71762306a36Sopenharmony_ci		val = llis_va[PL080_LLI_CCTL];
71862306a36Sopenharmony_ci		bytes = val & FTDMAC020_LLI_TRANSFER_SIZE_MASK;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		val = llis_va[PL080_LLI_CCTL];
72162306a36Sopenharmony_ci		val &= FTDMAC020_LLI_SRC_WIDTH_MSK;
72262306a36Sopenharmony_ci		val >>= FTDMAC020_LLI_SRC_WIDTH_SHIFT;
72362306a36Sopenharmony_ci	} else if (ch->pl080s) {
72462306a36Sopenharmony_ci		val = llis_va[PL080S_LLI_CCTL2];
72562306a36Sopenharmony_ci		bytes = val & PL080S_CONTROL_TRANSFER_SIZE_MASK;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		val = llis_va[PL080_LLI_CCTL];
72862306a36Sopenharmony_ci		val &= PL080_CONTROL_SWIDTH_MASK;
72962306a36Sopenharmony_ci		val >>= PL080_CONTROL_SWIDTH_SHIFT;
73062306a36Sopenharmony_ci	} else {
73162306a36Sopenharmony_ci		/* Plain PL08x */
73262306a36Sopenharmony_ci		val = llis_va[PL080_LLI_CCTL];
73362306a36Sopenharmony_ci		bytes = val & PL080_CONTROL_TRANSFER_SIZE_MASK;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		val &= PL080_CONTROL_SWIDTH_MASK;
73662306a36Sopenharmony_ci		val >>= PL080_CONTROL_SWIDTH_SHIFT;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	switch (val) {
74062306a36Sopenharmony_ci	case PL080_WIDTH_8BIT:
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case PL080_WIDTH_16BIT:
74362306a36Sopenharmony_ci		bytes *= 2;
74462306a36Sopenharmony_ci		break;
74562306a36Sopenharmony_ci	case PL080_WIDTH_32BIT:
74662306a36Sopenharmony_ci		bytes *= 4;
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	return bytes;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/* The channel should be paused when calling this */
75362306a36Sopenharmony_cistatic u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
75662306a36Sopenharmony_ci	const u32 *llis_va, *llis_va_limit;
75762306a36Sopenharmony_ci	struct pl08x_phy_chan *ch;
75862306a36Sopenharmony_ci	dma_addr_t llis_bus;
75962306a36Sopenharmony_ci	struct pl08x_txd *txd;
76062306a36Sopenharmony_ci	u32 llis_max_words;
76162306a36Sopenharmony_ci	size_t bytes;
76262306a36Sopenharmony_ci	u32 clli;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	ch = plchan->phychan;
76562306a36Sopenharmony_ci	txd = plchan->at;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (!ch || !txd)
76862306a36Sopenharmony_ci		return 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/*
77162306a36Sopenharmony_ci	 * Follow the LLIs to get the number of remaining
77262306a36Sopenharmony_ci	 * bytes in the currently active transaction.
77362306a36Sopenharmony_ci	 */
77462306a36Sopenharmony_ci	clli = readl(ch->reg_lli) & ~PL080_LLI_LM_AHB2;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* First get the remaining bytes in the active transfer */
77762306a36Sopenharmony_ci	bytes = get_bytes_in_phy_channel(ch);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!clli)
78062306a36Sopenharmony_ci		return bytes;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	llis_va = txd->llis_va;
78362306a36Sopenharmony_ci	llis_bus = txd->llis_bus;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	llis_max_words = pl08x->lli_words * MAX_NUM_TSFR_LLIS;
78662306a36Sopenharmony_ci	BUG_ON(clli < llis_bus || clli >= llis_bus +
78762306a36Sopenharmony_ci						sizeof(u32) * llis_max_words);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/*
79062306a36Sopenharmony_ci	 * Locate the next LLI - as this is an array,
79162306a36Sopenharmony_ci	 * it's simple maths to find.
79262306a36Sopenharmony_ci	 */
79362306a36Sopenharmony_ci	llis_va += (clli - llis_bus) / sizeof(u32);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	llis_va_limit = llis_va + llis_max_words;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	for (; llis_va < llis_va_limit; llis_va += pl08x->lli_words) {
79862306a36Sopenharmony_ci		bytes += get_bytes_in_lli(ch, llis_va);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		/*
80162306a36Sopenharmony_ci		 * A LLI pointer going backward terminates the LLI list
80262306a36Sopenharmony_ci		 */
80362306a36Sopenharmony_ci		if (llis_va[PL080_LLI_LLI] <= clli)
80462306a36Sopenharmony_ci			break;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return bytes;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci/*
81162306a36Sopenharmony_ci * Allocate a physical channel for a virtual channel
81262306a36Sopenharmony_ci *
81362306a36Sopenharmony_ci * Try to locate a physical channel to be used for this transfer. If all
81462306a36Sopenharmony_ci * are taken return NULL and the requester will have to cope by using
81562306a36Sopenharmony_ci * some fallback PIO mode or retrying later.
81662306a36Sopenharmony_ci */
81762306a36Sopenharmony_cistatic struct pl08x_phy_chan *
81862306a36Sopenharmony_cipl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
81962306a36Sopenharmony_ci		      struct pl08x_dma_chan *virt_chan)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct pl08x_phy_chan *ch = NULL;
82262306a36Sopenharmony_ci	unsigned long flags;
82362306a36Sopenharmony_ci	int i;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	for (i = 0; i < pl08x->vd->channels; i++) {
82662306a36Sopenharmony_ci		ch = &pl08x->phy_chans[i];
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		spin_lock_irqsave(&ch->lock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (!ch->locked && !ch->serving) {
83162306a36Sopenharmony_ci			ch->serving = virt_chan;
83262306a36Sopenharmony_ci			spin_unlock_irqrestore(&ch->lock, flags);
83362306a36Sopenharmony_ci			break;
83462306a36Sopenharmony_ci		}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, flags);
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (i == pl08x->vd->channels) {
84062306a36Sopenharmony_ci		/* No physical channel available, cope with it */
84162306a36Sopenharmony_ci		return NULL;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return ch;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci/* Mark the physical channel as free.  Note, this write is atomic. */
84862306a36Sopenharmony_cistatic inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
84962306a36Sopenharmony_ci					 struct pl08x_phy_chan *ch)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	ch->serving = NULL;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci/*
85562306a36Sopenharmony_ci * Try to allocate a physical channel.  When successful, assign it to
85662306a36Sopenharmony_ci * this virtual channel, and initiate the next descriptor.  The
85762306a36Sopenharmony_ci * virtual channel lock must be held at this point.
85862306a36Sopenharmony_ci */
85962306a36Sopenharmony_cistatic void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
86262306a36Sopenharmony_ci	struct pl08x_phy_chan *ch;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	ch = pl08x_get_phy_channel(pl08x, plchan);
86562306a36Sopenharmony_ci	if (!ch) {
86662306a36Sopenharmony_ci		dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
86762306a36Sopenharmony_ci		plchan->state = PL08X_CHAN_WAITING;
86862306a36Sopenharmony_ci		plchan->waiting_at = jiffies;
86962306a36Sopenharmony_ci		return;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
87362306a36Sopenharmony_ci		ch->id, plchan->name);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	plchan->phychan = ch;
87662306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_RUNNING;
87762306a36Sopenharmony_ci	pl08x_start_next_txd(plchan);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch,
88162306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n",
88662306a36Sopenharmony_ci		ch->id, plchan->name);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/*
88962306a36Sopenharmony_ci	 * We do this without taking the lock; we're really only concerned
89062306a36Sopenharmony_ci	 * about whether this pointer is NULL or not, and we're guaranteed
89162306a36Sopenharmony_ci	 * that this will only be called when it _already_ is non-NULL.
89262306a36Sopenharmony_ci	 */
89362306a36Sopenharmony_ci	ch->serving = plchan;
89462306a36Sopenharmony_ci	plchan->phychan = ch;
89562306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_RUNNING;
89662306a36Sopenharmony_ci	pl08x_start_next_txd(plchan);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/*
90062306a36Sopenharmony_ci * Free a physical DMA channel, potentially reallocating it to another
90162306a36Sopenharmony_ci * virtual channel if we have any pending.
90262306a36Sopenharmony_ci */
90362306a36Sopenharmony_cistatic void pl08x_phy_free(struct pl08x_dma_chan *plchan)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
90662306a36Sopenharmony_ci	struct pl08x_dma_chan *p, *next;
90762306a36Sopenharmony_ci	unsigned long waiting_at;
90862306a36Sopenharmony_ci retry:
90962306a36Sopenharmony_ci	next = NULL;
91062306a36Sopenharmony_ci	waiting_at = jiffies;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/*
91362306a36Sopenharmony_ci	 * Find a waiting virtual channel for the next transfer.
91462306a36Sopenharmony_ci	 * To be fair, time when each channel reached waiting state is compared
91562306a36Sopenharmony_ci	 * to select channel that is waiting for the longest time.
91662306a36Sopenharmony_ci	 */
91762306a36Sopenharmony_ci	list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node)
91862306a36Sopenharmony_ci		if (p->state == PL08X_CHAN_WAITING &&
91962306a36Sopenharmony_ci		    p->waiting_at <= waiting_at) {
92062306a36Sopenharmony_ci			next = p;
92162306a36Sopenharmony_ci			waiting_at = p->waiting_at;
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (!next && pl08x->has_slave) {
92562306a36Sopenharmony_ci		list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node)
92662306a36Sopenharmony_ci			if (p->state == PL08X_CHAN_WAITING &&
92762306a36Sopenharmony_ci			    p->waiting_at <= waiting_at) {
92862306a36Sopenharmony_ci				next = p;
92962306a36Sopenharmony_ci				waiting_at = p->waiting_at;
93062306a36Sopenharmony_ci			}
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Ensure that the physical channel is stopped */
93462306a36Sopenharmony_ci	pl08x_terminate_phy_chan(pl08x, plchan->phychan);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (next) {
93762306a36Sopenharmony_ci		bool success;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		/*
94062306a36Sopenharmony_ci		 * Eww.  We know this isn't going to deadlock
94162306a36Sopenharmony_ci		 * but lockdep probably doesn't.
94262306a36Sopenharmony_ci		 */
94362306a36Sopenharmony_ci		spin_lock(&next->vc.lock);
94462306a36Sopenharmony_ci		/* Re-check the state now that we have the lock */
94562306a36Sopenharmony_ci		success = next->state == PL08X_CHAN_WAITING;
94662306a36Sopenharmony_ci		if (success)
94762306a36Sopenharmony_ci			pl08x_phy_reassign_start(plchan->phychan, next);
94862306a36Sopenharmony_ci		spin_unlock(&next->vc.lock);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		/* If the state changed, try to find another channel */
95162306a36Sopenharmony_ci		if (!success)
95262306a36Sopenharmony_ci			goto retry;
95362306a36Sopenharmony_ci	} else {
95462306a36Sopenharmony_ci		/* No more jobs, so free up the physical channel */
95562306a36Sopenharmony_ci		pl08x_put_phy_channel(pl08x, plchan->phychan);
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	plchan->phychan = NULL;
95962306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_IDLE;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci/*
96362306a36Sopenharmony_ci * LLI handling
96462306a36Sopenharmony_ci */
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic inline unsigned int
96762306a36Sopenharmony_cipl08x_get_bytes_for_lli(struct pl08x_driver_data *pl08x,
96862306a36Sopenharmony_ci			u32 cctl,
96962306a36Sopenharmony_ci			bool source)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	u32 val;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
97462306a36Sopenharmony_ci		if (source)
97562306a36Sopenharmony_ci			val = (cctl & FTDMAC020_LLI_SRC_WIDTH_MSK) >>
97662306a36Sopenharmony_ci				FTDMAC020_LLI_SRC_WIDTH_SHIFT;
97762306a36Sopenharmony_ci		else
97862306a36Sopenharmony_ci			val = (cctl & FTDMAC020_LLI_DST_WIDTH_MSK) >>
97962306a36Sopenharmony_ci				FTDMAC020_LLI_DST_WIDTH_SHIFT;
98062306a36Sopenharmony_ci	} else {
98162306a36Sopenharmony_ci		if (source)
98262306a36Sopenharmony_ci			val = (cctl & PL080_CONTROL_SWIDTH_MASK) >>
98362306a36Sopenharmony_ci				PL080_CONTROL_SWIDTH_SHIFT;
98462306a36Sopenharmony_ci		else
98562306a36Sopenharmony_ci			val = (cctl & PL080_CONTROL_DWIDTH_MASK) >>
98662306a36Sopenharmony_ci				PL080_CONTROL_DWIDTH_SHIFT;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	switch (val) {
99062306a36Sopenharmony_ci	case PL080_WIDTH_8BIT:
99162306a36Sopenharmony_ci		return 1;
99262306a36Sopenharmony_ci	case PL080_WIDTH_16BIT:
99362306a36Sopenharmony_ci		return 2;
99462306a36Sopenharmony_ci	case PL080_WIDTH_32BIT:
99562306a36Sopenharmony_ci		return 4;
99662306a36Sopenharmony_ci	default:
99762306a36Sopenharmony_ci		break;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci	BUG();
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic inline u32 pl08x_lli_control_bits(struct pl08x_driver_data *pl08x,
100462306a36Sopenharmony_ci					 u32 cctl,
100562306a36Sopenharmony_ci					 u8 srcwidth, u8 dstwidth,
100662306a36Sopenharmony_ci					 size_t tsize)
100762306a36Sopenharmony_ci{
100862306a36Sopenharmony_ci	u32 retbits = cctl;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/*
101162306a36Sopenharmony_ci	 * Remove all src, dst and transfer size bits, then set the
101262306a36Sopenharmony_ci	 * width and size according to the parameters. The bit offsets
101362306a36Sopenharmony_ci	 * are different in the FTDMAC020 so we need to accound for this.
101462306a36Sopenharmony_ci	 */
101562306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
101662306a36Sopenharmony_ci		retbits &= ~FTDMAC020_LLI_DST_WIDTH_MSK;
101762306a36Sopenharmony_ci		retbits &= ~FTDMAC020_LLI_SRC_WIDTH_MSK;
101862306a36Sopenharmony_ci		retbits &= ~FTDMAC020_LLI_TRANSFER_SIZE_MASK;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		switch (srcwidth) {
102162306a36Sopenharmony_ci		case 1:
102262306a36Sopenharmony_ci			retbits |= PL080_WIDTH_8BIT <<
102362306a36Sopenharmony_ci				FTDMAC020_LLI_SRC_WIDTH_SHIFT;
102462306a36Sopenharmony_ci			break;
102562306a36Sopenharmony_ci		case 2:
102662306a36Sopenharmony_ci			retbits |= PL080_WIDTH_16BIT <<
102762306a36Sopenharmony_ci				FTDMAC020_LLI_SRC_WIDTH_SHIFT;
102862306a36Sopenharmony_ci			break;
102962306a36Sopenharmony_ci		case 4:
103062306a36Sopenharmony_ci			retbits |= PL080_WIDTH_32BIT <<
103162306a36Sopenharmony_ci				FTDMAC020_LLI_SRC_WIDTH_SHIFT;
103262306a36Sopenharmony_ci			break;
103362306a36Sopenharmony_ci		default:
103462306a36Sopenharmony_ci			BUG();
103562306a36Sopenharmony_ci			break;
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		switch (dstwidth) {
103962306a36Sopenharmony_ci		case 1:
104062306a36Sopenharmony_ci			retbits |= PL080_WIDTH_8BIT <<
104162306a36Sopenharmony_ci				FTDMAC020_LLI_DST_WIDTH_SHIFT;
104262306a36Sopenharmony_ci			break;
104362306a36Sopenharmony_ci		case 2:
104462306a36Sopenharmony_ci			retbits |= PL080_WIDTH_16BIT <<
104562306a36Sopenharmony_ci				FTDMAC020_LLI_DST_WIDTH_SHIFT;
104662306a36Sopenharmony_ci			break;
104762306a36Sopenharmony_ci		case 4:
104862306a36Sopenharmony_ci			retbits |= PL080_WIDTH_32BIT <<
104962306a36Sopenharmony_ci				FTDMAC020_LLI_DST_WIDTH_SHIFT;
105062306a36Sopenharmony_ci			break;
105162306a36Sopenharmony_ci		default:
105262306a36Sopenharmony_ci			BUG();
105362306a36Sopenharmony_ci			break;
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci		tsize &= FTDMAC020_LLI_TRANSFER_SIZE_MASK;
105762306a36Sopenharmony_ci		retbits |= tsize << FTDMAC020_LLI_TRANSFER_SIZE_SHIFT;
105862306a36Sopenharmony_ci	} else {
105962306a36Sopenharmony_ci		retbits &= ~PL080_CONTROL_DWIDTH_MASK;
106062306a36Sopenharmony_ci		retbits &= ~PL080_CONTROL_SWIDTH_MASK;
106162306a36Sopenharmony_ci		retbits &= ~PL080_CONTROL_TRANSFER_SIZE_MASK;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci		switch (srcwidth) {
106462306a36Sopenharmony_ci		case 1:
106562306a36Sopenharmony_ci			retbits |= PL080_WIDTH_8BIT <<
106662306a36Sopenharmony_ci				PL080_CONTROL_SWIDTH_SHIFT;
106762306a36Sopenharmony_ci			break;
106862306a36Sopenharmony_ci		case 2:
106962306a36Sopenharmony_ci			retbits |= PL080_WIDTH_16BIT <<
107062306a36Sopenharmony_ci				PL080_CONTROL_SWIDTH_SHIFT;
107162306a36Sopenharmony_ci			break;
107262306a36Sopenharmony_ci		case 4:
107362306a36Sopenharmony_ci			retbits |= PL080_WIDTH_32BIT <<
107462306a36Sopenharmony_ci				PL080_CONTROL_SWIDTH_SHIFT;
107562306a36Sopenharmony_ci			break;
107662306a36Sopenharmony_ci		default:
107762306a36Sopenharmony_ci			BUG();
107862306a36Sopenharmony_ci			break;
107962306a36Sopenharmony_ci		}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		switch (dstwidth) {
108262306a36Sopenharmony_ci		case 1:
108362306a36Sopenharmony_ci			retbits |= PL080_WIDTH_8BIT <<
108462306a36Sopenharmony_ci				PL080_CONTROL_DWIDTH_SHIFT;
108562306a36Sopenharmony_ci			break;
108662306a36Sopenharmony_ci		case 2:
108762306a36Sopenharmony_ci			retbits |= PL080_WIDTH_16BIT <<
108862306a36Sopenharmony_ci				PL080_CONTROL_DWIDTH_SHIFT;
108962306a36Sopenharmony_ci			break;
109062306a36Sopenharmony_ci		case 4:
109162306a36Sopenharmony_ci			retbits |= PL080_WIDTH_32BIT <<
109262306a36Sopenharmony_ci				PL080_CONTROL_DWIDTH_SHIFT;
109362306a36Sopenharmony_ci			break;
109462306a36Sopenharmony_ci		default:
109562306a36Sopenharmony_ci			BUG();
109662306a36Sopenharmony_ci			break;
109762306a36Sopenharmony_ci		}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		tsize &= PL080_CONTROL_TRANSFER_SIZE_MASK;
110062306a36Sopenharmony_ci		retbits |= tsize << PL080_CONTROL_TRANSFER_SIZE_SHIFT;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return retbits;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistruct pl08x_lli_build_data {
110762306a36Sopenharmony_ci	struct pl08x_txd *txd;
110862306a36Sopenharmony_ci	struct pl08x_bus_data srcbus;
110962306a36Sopenharmony_ci	struct pl08x_bus_data dstbus;
111062306a36Sopenharmony_ci	size_t remainder;
111162306a36Sopenharmony_ci	u32 lli_bus;
111262306a36Sopenharmony_ci};
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci/*
111562306a36Sopenharmony_ci * Autoselect a master bus to use for the transfer. Slave will be the chosen as
111662306a36Sopenharmony_ci * victim in case src & dest are not similarly aligned. i.e. If after aligning
111762306a36Sopenharmony_ci * masters address with width requirements of transfer (by sending few byte by
111862306a36Sopenharmony_ci * byte data), slave is still not aligned, then its width will be reduced to
111962306a36Sopenharmony_ci * BYTE.
112062306a36Sopenharmony_ci * - prefers the destination bus if both available
112162306a36Sopenharmony_ci * - prefers bus with fixed address (i.e. peripheral)
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_cistatic void pl08x_choose_master_bus(struct pl08x_driver_data *pl08x,
112462306a36Sopenharmony_ci				    struct pl08x_lli_build_data *bd,
112562306a36Sopenharmony_ci				    struct pl08x_bus_data **mbus,
112662306a36Sopenharmony_ci				    struct pl08x_bus_data **sbus,
112762306a36Sopenharmony_ci				    u32 cctl)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	bool dst_incr;
113062306a36Sopenharmony_ci	bool src_incr;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	/*
113362306a36Sopenharmony_ci	 * The FTDMAC020 only supports memory-to-memory transfer, so
113462306a36Sopenharmony_ci	 * source and destination always increase.
113562306a36Sopenharmony_ci	 */
113662306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
113762306a36Sopenharmony_ci		dst_incr = true;
113862306a36Sopenharmony_ci		src_incr = true;
113962306a36Sopenharmony_ci	} else {
114062306a36Sopenharmony_ci		dst_incr = !!(cctl & PL080_CONTROL_DST_INCR);
114162306a36Sopenharmony_ci		src_incr = !!(cctl & PL080_CONTROL_SRC_INCR);
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/*
114562306a36Sopenharmony_ci	 * If either bus is not advancing, i.e. it is a peripheral, that
114662306a36Sopenharmony_ci	 * one becomes master
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci	if (!dst_incr) {
114962306a36Sopenharmony_ci		*mbus = &bd->dstbus;
115062306a36Sopenharmony_ci		*sbus = &bd->srcbus;
115162306a36Sopenharmony_ci	} else if (!src_incr) {
115262306a36Sopenharmony_ci		*mbus = &bd->srcbus;
115362306a36Sopenharmony_ci		*sbus = &bd->dstbus;
115462306a36Sopenharmony_ci	} else {
115562306a36Sopenharmony_ci		if (bd->dstbus.buswidth >= bd->srcbus.buswidth) {
115662306a36Sopenharmony_ci			*mbus = &bd->dstbus;
115762306a36Sopenharmony_ci			*sbus = &bd->srcbus;
115862306a36Sopenharmony_ci		} else {
115962306a36Sopenharmony_ci			*mbus = &bd->srcbus;
116062306a36Sopenharmony_ci			*sbus = &bd->dstbus;
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci/*
116662306a36Sopenharmony_ci * Fills in one LLI for a certain transfer descriptor and advance the counter
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_cistatic void pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
116962306a36Sopenharmony_ci				    struct pl08x_lli_build_data *bd,
117062306a36Sopenharmony_ci				    int num_llis, int len, u32 cctl, u32 cctl2)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	u32 offset = num_llis * pl08x->lli_words;
117362306a36Sopenharmony_ci	u32 *llis_va = bd->txd->llis_va + offset;
117462306a36Sopenharmony_ci	dma_addr_t llis_bus = bd->txd->llis_bus;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Advance the offset to next LLI. */
117962306a36Sopenharmony_ci	offset += pl08x->lli_words;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	llis_va[PL080_LLI_SRC] = bd->srcbus.addr;
118262306a36Sopenharmony_ci	llis_va[PL080_LLI_DST] = bd->dstbus.addr;
118362306a36Sopenharmony_ci	llis_va[PL080_LLI_LLI] = (llis_bus + sizeof(u32) * offset);
118462306a36Sopenharmony_ci	llis_va[PL080_LLI_LLI] |= bd->lli_bus;
118562306a36Sopenharmony_ci	llis_va[PL080_LLI_CCTL] = cctl;
118662306a36Sopenharmony_ci	if (pl08x->vd->pl080s)
118762306a36Sopenharmony_ci		llis_va[PL080S_LLI_CCTL2] = cctl2;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
119062306a36Sopenharmony_ci		/* FIXME: only memcpy so far so both increase */
119162306a36Sopenharmony_ci		bd->srcbus.addr += len;
119262306a36Sopenharmony_ci		bd->dstbus.addr += len;
119362306a36Sopenharmony_ci	} else {
119462306a36Sopenharmony_ci		if (cctl & PL080_CONTROL_SRC_INCR)
119562306a36Sopenharmony_ci			bd->srcbus.addr += len;
119662306a36Sopenharmony_ci		if (cctl & PL080_CONTROL_DST_INCR)
119762306a36Sopenharmony_ci			bd->dstbus.addr += len;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	BUG_ON(bd->remainder < len);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	bd->remainder -= len;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic inline void prep_byte_width_lli(struct pl08x_driver_data *pl08x,
120662306a36Sopenharmony_ci			struct pl08x_lli_build_data *bd, u32 *cctl, u32 len,
120762306a36Sopenharmony_ci			int num_llis, size_t *total_bytes)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	*cctl = pl08x_lli_control_bits(pl08x, *cctl, 1, 1, len);
121062306a36Sopenharmony_ci	pl08x_fill_lli_for_desc(pl08x, bd, num_llis, len, *cctl, len);
121162306a36Sopenharmony_ci	(*total_bytes) += len;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci#if 1
121562306a36Sopenharmony_cistatic void pl08x_dump_lli(struct pl08x_driver_data *pl08x,
121662306a36Sopenharmony_ci			   const u32 *llis_va, int num_llis)
121762306a36Sopenharmony_ci{
121862306a36Sopenharmony_ci	int i;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (pl08x->vd->pl080s) {
122162306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev,
122262306a36Sopenharmony_ci			"%-3s %-9s  %-10s %-10s %-10s %-10s %s\n",
122362306a36Sopenharmony_ci			"lli", "", "csrc", "cdst", "clli", "cctl", "cctl2");
122462306a36Sopenharmony_ci		for (i = 0; i < num_llis; i++) {
122562306a36Sopenharmony_ci			dev_vdbg(&pl08x->adev->dev,
122662306a36Sopenharmony_ci				"%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
122762306a36Sopenharmony_ci				i, llis_va, llis_va[PL080_LLI_SRC],
122862306a36Sopenharmony_ci				llis_va[PL080_LLI_DST], llis_va[PL080_LLI_LLI],
122962306a36Sopenharmony_ci				llis_va[PL080_LLI_CCTL],
123062306a36Sopenharmony_ci				llis_va[PL080S_LLI_CCTL2]);
123162306a36Sopenharmony_ci			llis_va += pl08x->lli_words;
123262306a36Sopenharmony_ci		}
123362306a36Sopenharmony_ci	} else {
123462306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev,
123562306a36Sopenharmony_ci			"%-3s %-9s  %-10s %-10s %-10s %s\n",
123662306a36Sopenharmony_ci			"lli", "", "csrc", "cdst", "clli", "cctl");
123762306a36Sopenharmony_ci		for (i = 0; i < num_llis; i++) {
123862306a36Sopenharmony_ci			dev_vdbg(&pl08x->adev->dev,
123962306a36Sopenharmony_ci				"%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
124062306a36Sopenharmony_ci				i, llis_va, llis_va[PL080_LLI_SRC],
124162306a36Sopenharmony_ci				llis_va[PL080_LLI_DST], llis_va[PL080_LLI_LLI],
124262306a36Sopenharmony_ci				llis_va[PL080_LLI_CCTL]);
124362306a36Sopenharmony_ci			llis_va += pl08x->lli_words;
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci#else
124862306a36Sopenharmony_cistatic inline void pl08x_dump_lli(struct pl08x_driver_data *pl08x,
124962306a36Sopenharmony_ci				  const u32 *llis_va, int num_llis) {}
125062306a36Sopenharmony_ci#endif
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci/*
125362306a36Sopenharmony_ci * This fills in the table of LLIs for the transfer descriptor
125462306a36Sopenharmony_ci * Note that we assume we never have to change the burst sizes
125562306a36Sopenharmony_ci * Return 0 for error
125662306a36Sopenharmony_ci */
125762306a36Sopenharmony_cistatic int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
125862306a36Sopenharmony_ci			      struct pl08x_txd *txd)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct pl08x_bus_data *mbus, *sbus;
126162306a36Sopenharmony_ci	struct pl08x_lli_build_data bd;
126262306a36Sopenharmony_ci	int num_llis = 0;
126362306a36Sopenharmony_ci	u32 cctl, early_bytes = 0;
126462306a36Sopenharmony_ci	size_t max_bytes_per_lli, total_bytes;
126562306a36Sopenharmony_ci	u32 *llis_va, *last_lli;
126662306a36Sopenharmony_ci	struct pl08x_sg *dsg;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus);
126962306a36Sopenharmony_ci	if (!txd->llis_va) {
127062306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__);
127162306a36Sopenharmony_ci		return 0;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	bd.txd = txd;
127562306a36Sopenharmony_ci	bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
127662306a36Sopenharmony_ci	cctl = txd->cctl;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	/* Find maximum width of the source bus */
127962306a36Sopenharmony_ci	bd.srcbus.maxwidth = pl08x_get_bytes_for_lli(pl08x, cctl, true);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	/* Find maximum width of the destination bus */
128262306a36Sopenharmony_ci	bd.dstbus.maxwidth = pl08x_get_bytes_for_lli(pl08x, cctl, false);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	list_for_each_entry(dsg, &txd->dsg_list, node) {
128562306a36Sopenharmony_ci		total_bytes = 0;
128662306a36Sopenharmony_ci		cctl = txd->cctl;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		bd.srcbus.addr = dsg->src_addr;
128962306a36Sopenharmony_ci		bd.dstbus.addr = dsg->dst_addr;
129062306a36Sopenharmony_ci		bd.remainder = dsg->len;
129162306a36Sopenharmony_ci		bd.srcbus.buswidth = bd.srcbus.maxwidth;
129262306a36Sopenharmony_ci		bd.dstbus.buswidth = bd.dstbus.maxwidth;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci		pl08x_choose_master_bus(pl08x, &bd, &mbus, &sbus, cctl);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev,
129762306a36Sopenharmony_ci			"src=0x%08llx%s/%u dst=0x%08llx%s/%u len=%zu\n",
129862306a36Sopenharmony_ci			(u64)bd.srcbus.addr,
129962306a36Sopenharmony_ci			cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
130062306a36Sopenharmony_ci			bd.srcbus.buswidth,
130162306a36Sopenharmony_ci			(u64)bd.dstbus.addr,
130262306a36Sopenharmony_ci			cctl & PL080_CONTROL_DST_INCR ? "+" : "",
130362306a36Sopenharmony_ci			bd.dstbus.buswidth,
130462306a36Sopenharmony_ci			bd.remainder);
130562306a36Sopenharmony_ci		dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
130662306a36Sopenharmony_ci			mbus == &bd.srcbus ? "src" : "dst",
130762306a36Sopenharmony_ci			sbus == &bd.srcbus ? "src" : "dst");
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci		/*
131062306a36Sopenharmony_ci		 * Zero length is only allowed if all these requirements are
131162306a36Sopenharmony_ci		 * met:
131262306a36Sopenharmony_ci		 * - flow controller is peripheral.
131362306a36Sopenharmony_ci		 * - src.addr is aligned to src.width
131462306a36Sopenharmony_ci		 * - dst.addr is aligned to dst.width
131562306a36Sopenharmony_ci		 *
131662306a36Sopenharmony_ci		 * sg_len == 1 should be true, as there can be two cases here:
131762306a36Sopenharmony_ci		 *
131862306a36Sopenharmony_ci		 * - Memory addresses are contiguous and are not scattered.
131962306a36Sopenharmony_ci		 *   Here, Only one sg will be passed by user driver, with
132062306a36Sopenharmony_ci		 *   memory address and zero length. We pass this to controller
132162306a36Sopenharmony_ci		 *   and after the transfer it will receive the last burst
132262306a36Sopenharmony_ci		 *   request from peripheral and so transfer finishes.
132362306a36Sopenharmony_ci		 *
132462306a36Sopenharmony_ci		 * - Memory addresses are scattered and are not contiguous.
132562306a36Sopenharmony_ci		 *   Here, Obviously as DMA controller doesn't know when a lli's
132662306a36Sopenharmony_ci		 *   transfer gets over, it can't load next lli. So in this
132762306a36Sopenharmony_ci		 *   case, there has to be an assumption that only one lli is
132862306a36Sopenharmony_ci		 *   supported. Thus, we can't have scattered addresses.
132962306a36Sopenharmony_ci		 */
133062306a36Sopenharmony_ci		if (!bd.remainder) {
133162306a36Sopenharmony_ci			u32 fc;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci			/* FTDMAC020 only does memory-to-memory */
133462306a36Sopenharmony_ci			if (pl08x->vd->ftdmac020)
133562306a36Sopenharmony_ci				fc = PL080_FLOW_MEM2MEM;
133662306a36Sopenharmony_ci			else
133762306a36Sopenharmony_ci				fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >>
133862306a36Sopenharmony_ci					PL080_CONFIG_FLOW_CONTROL_SHIFT;
133962306a36Sopenharmony_ci			if (!((fc >= PL080_FLOW_SRC2DST_DST) &&
134062306a36Sopenharmony_ci					(fc <= PL080_FLOW_SRC2DST_SRC))) {
134162306a36Sopenharmony_ci				dev_err(&pl08x->adev->dev, "%s sg len can't be zero",
134262306a36Sopenharmony_ci					__func__);
134362306a36Sopenharmony_ci				return 0;
134462306a36Sopenharmony_ci			}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci			if (!IS_BUS_ALIGNED(&bd.srcbus) ||
134762306a36Sopenharmony_ci				!IS_BUS_ALIGNED(&bd.dstbus)) {
134862306a36Sopenharmony_ci				dev_err(&pl08x->adev->dev,
134962306a36Sopenharmony_ci					"%s src & dst address must be aligned to src"
135062306a36Sopenharmony_ci					" & dst width if peripheral is flow controller",
135162306a36Sopenharmony_ci					__func__);
135262306a36Sopenharmony_ci				return 0;
135362306a36Sopenharmony_ci			}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci			cctl = pl08x_lli_control_bits(pl08x, cctl,
135662306a36Sopenharmony_ci					bd.srcbus.buswidth, bd.dstbus.buswidth,
135762306a36Sopenharmony_ci					0);
135862306a36Sopenharmony_ci			pl08x_fill_lli_for_desc(pl08x, &bd, num_llis++,
135962306a36Sopenharmony_ci					0, cctl, 0);
136062306a36Sopenharmony_ci			break;
136162306a36Sopenharmony_ci		}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci		/*
136462306a36Sopenharmony_ci		 * Send byte by byte for following cases
136562306a36Sopenharmony_ci		 * - Less than a bus width available
136662306a36Sopenharmony_ci		 * - until master bus is aligned
136762306a36Sopenharmony_ci		 */
136862306a36Sopenharmony_ci		if (bd.remainder < mbus->buswidth)
136962306a36Sopenharmony_ci			early_bytes = bd.remainder;
137062306a36Sopenharmony_ci		else if (!IS_BUS_ALIGNED(mbus)) {
137162306a36Sopenharmony_ci			early_bytes = mbus->buswidth -
137262306a36Sopenharmony_ci				(mbus->addr & (mbus->buswidth - 1));
137362306a36Sopenharmony_ci			if ((bd.remainder - early_bytes) < mbus->buswidth)
137462306a36Sopenharmony_ci				early_bytes = bd.remainder;
137562306a36Sopenharmony_ci		}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci		if (early_bytes) {
137862306a36Sopenharmony_ci			dev_vdbg(&pl08x->adev->dev,
137962306a36Sopenharmony_ci				"%s byte width LLIs (remain 0x%08zx)\n",
138062306a36Sopenharmony_ci				__func__, bd.remainder);
138162306a36Sopenharmony_ci			prep_byte_width_lli(pl08x, &bd, &cctl, early_bytes,
138262306a36Sopenharmony_ci				num_llis++, &total_bytes);
138362306a36Sopenharmony_ci		}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci		if (bd.remainder) {
138662306a36Sopenharmony_ci			/*
138762306a36Sopenharmony_ci			 * Master now aligned
138862306a36Sopenharmony_ci			 * - if slave is not then we must set its width down
138962306a36Sopenharmony_ci			 */
139062306a36Sopenharmony_ci			if (!IS_BUS_ALIGNED(sbus)) {
139162306a36Sopenharmony_ci				dev_dbg(&pl08x->adev->dev,
139262306a36Sopenharmony_ci					"%s set down bus width to one byte\n",
139362306a36Sopenharmony_ci					__func__);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci				sbus->buswidth = 1;
139662306a36Sopenharmony_ci			}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci			/*
139962306a36Sopenharmony_ci			 * Bytes transferred = tsize * src width, not
140062306a36Sopenharmony_ci			 * MIN(buswidths)
140162306a36Sopenharmony_ci			 */
140262306a36Sopenharmony_ci			max_bytes_per_lli = bd.srcbus.buswidth *
140362306a36Sopenharmony_ci						pl08x->vd->max_transfer_size;
140462306a36Sopenharmony_ci			dev_vdbg(&pl08x->adev->dev,
140562306a36Sopenharmony_ci				"%s max bytes per lli = %zu\n",
140662306a36Sopenharmony_ci				__func__, max_bytes_per_lli);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci			/*
140962306a36Sopenharmony_ci			 * Make largest possible LLIs until less than one bus
141062306a36Sopenharmony_ci			 * width left
141162306a36Sopenharmony_ci			 */
141262306a36Sopenharmony_ci			while (bd.remainder > (mbus->buswidth - 1)) {
141362306a36Sopenharmony_ci				size_t lli_len, tsize, width;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci				/*
141662306a36Sopenharmony_ci				 * If enough left try to send max possible,
141762306a36Sopenharmony_ci				 * otherwise try to send the remainder
141862306a36Sopenharmony_ci				 */
141962306a36Sopenharmony_ci				lli_len = min(bd.remainder, max_bytes_per_lli);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci				/*
142262306a36Sopenharmony_ci				 * Check against maximum bus alignment:
142362306a36Sopenharmony_ci				 * Calculate actual transfer size in relation to
142462306a36Sopenharmony_ci				 * bus width an get a maximum remainder of the
142562306a36Sopenharmony_ci				 * highest bus width - 1
142662306a36Sopenharmony_ci				 */
142762306a36Sopenharmony_ci				width = max(mbus->buswidth, sbus->buswidth);
142862306a36Sopenharmony_ci				lli_len = (lli_len / width) * width;
142962306a36Sopenharmony_ci				tsize = lli_len / bd.srcbus.buswidth;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci				dev_vdbg(&pl08x->adev->dev,
143262306a36Sopenharmony_ci					"%s fill lli with single lli chunk of "
143362306a36Sopenharmony_ci					"size 0x%08zx (remainder 0x%08zx)\n",
143462306a36Sopenharmony_ci					__func__, lli_len, bd.remainder);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci				cctl = pl08x_lli_control_bits(pl08x, cctl,
143762306a36Sopenharmony_ci					bd.srcbus.buswidth, bd.dstbus.buswidth,
143862306a36Sopenharmony_ci					tsize);
143962306a36Sopenharmony_ci				pl08x_fill_lli_for_desc(pl08x, &bd, num_llis++,
144062306a36Sopenharmony_ci						lli_len, cctl, tsize);
144162306a36Sopenharmony_ci				total_bytes += lli_len;
144262306a36Sopenharmony_ci			}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci			/*
144562306a36Sopenharmony_ci			 * Send any odd bytes
144662306a36Sopenharmony_ci			 */
144762306a36Sopenharmony_ci			if (bd.remainder) {
144862306a36Sopenharmony_ci				dev_vdbg(&pl08x->adev->dev,
144962306a36Sopenharmony_ci					"%s align with boundary, send odd bytes (remain %zu)\n",
145062306a36Sopenharmony_ci					__func__, bd.remainder);
145162306a36Sopenharmony_ci				prep_byte_width_lli(pl08x, &bd, &cctl,
145262306a36Sopenharmony_ci					bd.remainder, num_llis++, &total_bytes);
145362306a36Sopenharmony_ci			}
145462306a36Sopenharmony_ci		}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci		if (total_bytes != dsg->len) {
145762306a36Sopenharmony_ci			dev_err(&pl08x->adev->dev,
145862306a36Sopenharmony_ci				"%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n",
145962306a36Sopenharmony_ci				__func__, total_bytes, dsg->len);
146062306a36Sopenharmony_ci			return 0;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		if (num_llis >= MAX_NUM_TSFR_LLIS) {
146462306a36Sopenharmony_ci			dev_err(&pl08x->adev->dev,
146562306a36Sopenharmony_ci				"%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n",
146662306a36Sopenharmony_ci				__func__, MAX_NUM_TSFR_LLIS);
146762306a36Sopenharmony_ci			return 0;
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	llis_va = txd->llis_va;
147262306a36Sopenharmony_ci	last_lli = llis_va + (num_llis - 1) * pl08x->lli_words;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	if (txd->cyclic) {
147562306a36Sopenharmony_ci		/* Link back to the first LLI. */
147662306a36Sopenharmony_ci		last_lli[PL080_LLI_LLI] = txd->llis_bus | bd.lli_bus;
147762306a36Sopenharmony_ci	} else {
147862306a36Sopenharmony_ci		/* The final LLI terminates the LLI. */
147962306a36Sopenharmony_ci		last_lli[PL080_LLI_LLI] = 0;
148062306a36Sopenharmony_ci		/* The final LLI element shall also fire an interrupt. */
148162306a36Sopenharmony_ci		if (pl08x->vd->ftdmac020)
148262306a36Sopenharmony_ci			last_lli[PL080_LLI_CCTL] &= ~FTDMAC020_LLI_TC_MSK;
148362306a36Sopenharmony_ci		else
148462306a36Sopenharmony_ci			last_lli[PL080_LLI_CCTL] |= PL080_CONTROL_TC_IRQ_EN;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	pl08x_dump_lli(pl08x, llis_va, num_llis);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return num_llis;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic void pl08x_free_txd(struct pl08x_driver_data *pl08x,
149362306a36Sopenharmony_ci			   struct pl08x_txd *txd)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct pl08x_sg *dsg, *_dsg;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	if (txd->llis_va)
149862306a36Sopenharmony_ci		dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) {
150162306a36Sopenharmony_ci		list_del(&dsg->node);
150262306a36Sopenharmony_ci		kfree(dsg);
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	kfree(txd);
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic void pl08x_desc_free(struct virt_dma_desc *vd)
150962306a36Sopenharmony_ci{
151062306a36Sopenharmony_ci	struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
151162306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan);
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	dma_descriptor_unmap(&vd->tx);
151462306a36Sopenharmony_ci	if (!txd->done)
151562306a36Sopenharmony_ci		pl08x_release_mux(plchan);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	pl08x_free_txd(plchan->host, txd);
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
152162306a36Sopenharmony_ci				struct pl08x_dma_chan *plchan)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	LIST_HEAD(head);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	vchan_get_all_descriptors(&plchan->vc, &head);
152662306a36Sopenharmony_ci	vchan_dma_desc_free_list(&plchan->vc, &head);
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci/*
153062306a36Sopenharmony_ci * The DMA ENGINE API
153162306a36Sopenharmony_ci */
153262306a36Sopenharmony_cistatic void pl08x_free_chan_resources(struct dma_chan *chan)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	/* Ensure all queued descriptors are freed */
153562306a36Sopenharmony_ci	vchan_free_chan_resources(to_virt_chan(chan));
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci/*
153962306a36Sopenharmony_ci * Code accessing dma_async_is_complete() in a tight loop may give problems.
154062306a36Sopenharmony_ci * If slaves are relying on interrupts to signal completion this function
154162306a36Sopenharmony_ci * must not be called with interrupts disabled.
154262306a36Sopenharmony_ci */
154362306a36Sopenharmony_cistatic enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
154462306a36Sopenharmony_ci		dma_cookie_t cookie, struct dma_tx_state *txstate)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
154762306a36Sopenharmony_ci	struct virt_dma_desc *vd;
154862306a36Sopenharmony_ci	unsigned long flags;
154962306a36Sopenharmony_ci	enum dma_status ret;
155062306a36Sopenharmony_ci	size_t bytes = 0;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
155362306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
155462306a36Sopenharmony_ci		return ret;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	/*
155762306a36Sopenharmony_ci	 * There's no point calculating the residue if there's
155862306a36Sopenharmony_ci	 * no txstate to store the value.
155962306a36Sopenharmony_ci	 */
156062306a36Sopenharmony_ci	if (!txstate) {
156162306a36Sopenharmony_ci		if (plchan->state == PL08X_CHAN_PAUSED)
156262306a36Sopenharmony_ci			ret = DMA_PAUSED;
156362306a36Sopenharmony_ci		return ret;
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	spin_lock_irqsave(&plchan->vc.lock, flags);
156762306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
156862306a36Sopenharmony_ci	if (ret != DMA_COMPLETE) {
156962306a36Sopenharmony_ci		vd = vchan_find_desc(&plchan->vc, cookie);
157062306a36Sopenharmony_ci		if (vd) {
157162306a36Sopenharmony_ci			/* On the issued list, so hasn't been processed yet */
157262306a36Sopenharmony_ci			struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
157362306a36Sopenharmony_ci			struct pl08x_sg *dsg;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci			list_for_each_entry(dsg, &txd->dsg_list, node)
157662306a36Sopenharmony_ci				bytes += dsg->len;
157762306a36Sopenharmony_ci		} else {
157862306a36Sopenharmony_ci			bytes = pl08x_getbytes_chan(plchan);
157962306a36Sopenharmony_ci		}
158062306a36Sopenharmony_ci	}
158162306a36Sopenharmony_ci	spin_unlock_irqrestore(&plchan->vc.lock, flags);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	/*
158462306a36Sopenharmony_ci	 * This cookie not complete yet
158562306a36Sopenharmony_ci	 * Get number of bytes left in the active transactions and queue
158662306a36Sopenharmony_ci	 */
158762306a36Sopenharmony_ci	dma_set_residue(txstate, bytes);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	if (plchan->state == PL08X_CHAN_PAUSED && ret == DMA_IN_PROGRESS)
159062306a36Sopenharmony_ci		ret = DMA_PAUSED;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	/* Whether waiting or running, we're in progress */
159362306a36Sopenharmony_ci	return ret;
159462306a36Sopenharmony_ci}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci/* PrimeCell DMA extension */
159762306a36Sopenharmony_cistruct burst_table {
159862306a36Sopenharmony_ci	u32 burstwords;
159962306a36Sopenharmony_ci	u32 reg;
160062306a36Sopenharmony_ci};
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_cistatic const struct burst_table burst_sizes[] = {
160362306a36Sopenharmony_ci	{
160462306a36Sopenharmony_ci		.burstwords = 256,
160562306a36Sopenharmony_ci		.reg = PL080_BSIZE_256,
160662306a36Sopenharmony_ci	},
160762306a36Sopenharmony_ci	{
160862306a36Sopenharmony_ci		.burstwords = 128,
160962306a36Sopenharmony_ci		.reg = PL080_BSIZE_128,
161062306a36Sopenharmony_ci	},
161162306a36Sopenharmony_ci	{
161262306a36Sopenharmony_ci		.burstwords = 64,
161362306a36Sopenharmony_ci		.reg = PL080_BSIZE_64,
161462306a36Sopenharmony_ci	},
161562306a36Sopenharmony_ci	{
161662306a36Sopenharmony_ci		.burstwords = 32,
161762306a36Sopenharmony_ci		.reg = PL080_BSIZE_32,
161862306a36Sopenharmony_ci	},
161962306a36Sopenharmony_ci	{
162062306a36Sopenharmony_ci		.burstwords = 16,
162162306a36Sopenharmony_ci		.reg = PL080_BSIZE_16,
162262306a36Sopenharmony_ci	},
162362306a36Sopenharmony_ci	{
162462306a36Sopenharmony_ci		.burstwords = 8,
162562306a36Sopenharmony_ci		.reg = PL080_BSIZE_8,
162662306a36Sopenharmony_ci	},
162762306a36Sopenharmony_ci	{
162862306a36Sopenharmony_ci		.burstwords = 4,
162962306a36Sopenharmony_ci		.reg = PL080_BSIZE_4,
163062306a36Sopenharmony_ci	},
163162306a36Sopenharmony_ci	{
163262306a36Sopenharmony_ci		.burstwords = 0,
163362306a36Sopenharmony_ci		.reg = PL080_BSIZE_1,
163462306a36Sopenharmony_ci	},
163562306a36Sopenharmony_ci};
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci/*
163862306a36Sopenharmony_ci * Given the source and destination available bus masks, select which
163962306a36Sopenharmony_ci * will be routed to each port.  We try to have source and destination
164062306a36Sopenharmony_ci * on separate ports, but always respect the allowable settings.
164162306a36Sopenharmony_ci */
164262306a36Sopenharmony_cistatic u32 pl08x_select_bus(bool ftdmac020, u8 src, u8 dst)
164362306a36Sopenharmony_ci{
164462306a36Sopenharmony_ci	u32 cctl = 0;
164562306a36Sopenharmony_ci	u32 dst_ahb2;
164662306a36Sopenharmony_ci	u32 src_ahb2;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	/* The FTDMAC020 use different bits to indicate src/dst bus */
164962306a36Sopenharmony_ci	if (ftdmac020) {
165062306a36Sopenharmony_ci		dst_ahb2 = FTDMAC020_LLI_DST_SEL;
165162306a36Sopenharmony_ci		src_ahb2 = FTDMAC020_LLI_SRC_SEL;
165262306a36Sopenharmony_ci	} else {
165362306a36Sopenharmony_ci		dst_ahb2 = PL080_CONTROL_DST_AHB2;
165462306a36Sopenharmony_ci		src_ahb2 = PL080_CONTROL_SRC_AHB2;
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
165862306a36Sopenharmony_ci		cctl |= dst_ahb2;
165962306a36Sopenharmony_ci	if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
166062306a36Sopenharmony_ci		cctl |= src_ahb2;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	return cctl;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic u32 pl08x_cctl(u32 cctl)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
166862306a36Sopenharmony_ci		  PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
166962306a36Sopenharmony_ci		  PL080_CONTROL_PROT_MASK);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	/* Access the cell in privileged mode, non-bufferable, non-cacheable */
167262306a36Sopenharmony_ci	return cctl | PL080_CONTROL_PROT_SYS;
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic u32 pl08x_width(enum dma_slave_buswidth width)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	switch (width) {
167862306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_1_BYTE:
167962306a36Sopenharmony_ci		return PL080_WIDTH_8BIT;
168062306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_2_BYTES:
168162306a36Sopenharmony_ci		return PL080_WIDTH_16BIT;
168262306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_4_BYTES:
168362306a36Sopenharmony_ci		return PL080_WIDTH_32BIT;
168462306a36Sopenharmony_ci	default:
168562306a36Sopenharmony_ci		return ~0;
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic u32 pl08x_burst(u32 maxburst)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	int i;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
169462306a36Sopenharmony_ci		if (burst_sizes[i].burstwords <= maxburst)
169562306a36Sopenharmony_ci			break;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	return burst_sizes[i].reg;
169862306a36Sopenharmony_ci}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_cistatic u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan,
170162306a36Sopenharmony_ci	enum dma_slave_buswidth addr_width, u32 maxburst)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	u32 width, burst, cctl = 0;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	width = pl08x_width(addr_width);
170662306a36Sopenharmony_ci	if (width == ~0)
170762306a36Sopenharmony_ci		return ~0;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
171062306a36Sopenharmony_ci	cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/*
171362306a36Sopenharmony_ci	 * If this channel will only request single transfers, set this
171462306a36Sopenharmony_ci	 * down to ONE element.  Also select one element if no maxburst
171562306a36Sopenharmony_ci	 * is specified.
171662306a36Sopenharmony_ci	 */
171762306a36Sopenharmony_ci	if (plchan->cd->single)
171862306a36Sopenharmony_ci		maxburst = 1;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	burst = pl08x_burst(maxburst);
172162306a36Sopenharmony_ci	cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
172262306a36Sopenharmony_ci	cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	return pl08x_cctl(cctl);
172562306a36Sopenharmony_ci}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci/*
172862306a36Sopenharmony_ci * Slave transactions callback to the slave device to allow
172962306a36Sopenharmony_ci * synchronization of slave DMA signals with the DMAC enable
173062306a36Sopenharmony_ci */
173162306a36Sopenharmony_cistatic void pl08x_issue_pending(struct dma_chan *chan)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
173462306a36Sopenharmony_ci	unsigned long flags;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	spin_lock_irqsave(&plchan->vc.lock, flags);
173762306a36Sopenharmony_ci	if (vchan_issue_pending(&plchan->vc)) {
173862306a36Sopenharmony_ci		if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING)
173962306a36Sopenharmony_ci			pl08x_phy_alloc_and_start(plchan);
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci	spin_unlock_irqrestore(&plchan->vc.lock, flags);
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (txd)
174962306a36Sopenharmony_ci		INIT_LIST_HEAD(&txd->dsg_list);
175062306a36Sopenharmony_ci	return txd;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic u32 pl08x_memcpy_cctl(struct pl08x_driver_data *pl08x)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	u32 cctl = 0;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	/* Conjure cctl */
175862306a36Sopenharmony_ci	switch (pl08x->pd->memcpy_burst_size) {
175962306a36Sopenharmony_ci	default:
176062306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
176162306a36Sopenharmony_ci			"illegal burst size for memcpy, set to 1\n");
176262306a36Sopenharmony_ci		fallthrough;
176362306a36Sopenharmony_ci	case PL08X_BURST_SZ_1:
176462306a36Sopenharmony_ci		cctl |= PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT |
176562306a36Sopenharmony_ci			PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT;
176662306a36Sopenharmony_ci		break;
176762306a36Sopenharmony_ci	case PL08X_BURST_SZ_4:
176862306a36Sopenharmony_ci		cctl |= PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT |
176962306a36Sopenharmony_ci			PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT;
177062306a36Sopenharmony_ci		break;
177162306a36Sopenharmony_ci	case PL08X_BURST_SZ_8:
177262306a36Sopenharmony_ci		cctl |= PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT |
177362306a36Sopenharmony_ci			PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT;
177462306a36Sopenharmony_ci		break;
177562306a36Sopenharmony_ci	case PL08X_BURST_SZ_16:
177662306a36Sopenharmony_ci		cctl |= PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT |
177762306a36Sopenharmony_ci			PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT;
177862306a36Sopenharmony_ci		break;
177962306a36Sopenharmony_ci	case PL08X_BURST_SZ_32:
178062306a36Sopenharmony_ci		cctl |= PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT |
178162306a36Sopenharmony_ci			PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT;
178262306a36Sopenharmony_ci		break;
178362306a36Sopenharmony_ci	case PL08X_BURST_SZ_64:
178462306a36Sopenharmony_ci		cctl |= PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT |
178562306a36Sopenharmony_ci			PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT;
178662306a36Sopenharmony_ci		break;
178762306a36Sopenharmony_ci	case PL08X_BURST_SZ_128:
178862306a36Sopenharmony_ci		cctl |= PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT |
178962306a36Sopenharmony_ci			PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT;
179062306a36Sopenharmony_ci		break;
179162306a36Sopenharmony_ci	case PL08X_BURST_SZ_256:
179262306a36Sopenharmony_ci		cctl |= PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT |
179362306a36Sopenharmony_ci			PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT;
179462306a36Sopenharmony_ci		break;
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	switch (pl08x->pd->memcpy_bus_width) {
179862306a36Sopenharmony_ci	default:
179962306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
180062306a36Sopenharmony_ci			"illegal bus width for memcpy, set to 8 bits\n");
180162306a36Sopenharmony_ci		fallthrough;
180262306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_8_BITS:
180362306a36Sopenharmony_ci		cctl |= PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT |
180462306a36Sopenharmony_ci			PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT;
180562306a36Sopenharmony_ci		break;
180662306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_16_BITS:
180762306a36Sopenharmony_ci		cctl |= PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT |
180862306a36Sopenharmony_ci			PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT;
180962306a36Sopenharmony_ci		break;
181062306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_32_BITS:
181162306a36Sopenharmony_ci		cctl |= PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT |
181262306a36Sopenharmony_ci			PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT;
181362306a36Sopenharmony_ci		break;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/* Protection flags */
181762306a36Sopenharmony_ci	if (pl08x->pd->memcpy_prot_buff)
181862306a36Sopenharmony_ci		cctl |= PL080_CONTROL_PROT_BUFF;
181962306a36Sopenharmony_ci	if (pl08x->pd->memcpy_prot_cache)
182062306a36Sopenharmony_ci		cctl |= PL080_CONTROL_PROT_CACHE;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	/* We are the kernel, so we are in privileged mode */
182362306a36Sopenharmony_ci	cctl |= PL080_CONTROL_PROT_SYS;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	/* Both to be incremented or the code will break */
182662306a36Sopenharmony_ci	cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	if (pl08x->vd->dualmaster)
182962306a36Sopenharmony_ci		cctl |= pl08x_select_bus(false,
183062306a36Sopenharmony_ci					 pl08x->mem_buses,
183162306a36Sopenharmony_ci					 pl08x->mem_buses);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	return cctl;
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic u32 pl08x_ftdmac020_memcpy_cctl(struct pl08x_driver_data *pl08x)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci	u32 cctl = 0;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* Conjure cctl */
184162306a36Sopenharmony_ci	switch (pl08x->pd->memcpy_bus_width) {
184262306a36Sopenharmony_ci	default:
184362306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
184462306a36Sopenharmony_ci			"illegal bus width for memcpy, set to 8 bits\n");
184562306a36Sopenharmony_ci		fallthrough;
184662306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_8_BITS:
184762306a36Sopenharmony_ci		cctl |= PL080_WIDTH_8BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
184862306a36Sopenharmony_ci			PL080_WIDTH_8BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT;
184962306a36Sopenharmony_ci		break;
185062306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_16_BITS:
185162306a36Sopenharmony_ci		cctl |= PL080_WIDTH_16BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
185262306a36Sopenharmony_ci			PL080_WIDTH_16BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT;
185362306a36Sopenharmony_ci		break;
185462306a36Sopenharmony_ci	case PL08X_BUS_WIDTH_32_BITS:
185562306a36Sopenharmony_ci		cctl |= PL080_WIDTH_32BIT << FTDMAC020_LLI_SRC_WIDTH_SHIFT |
185662306a36Sopenharmony_ci			PL080_WIDTH_32BIT << FTDMAC020_LLI_DST_WIDTH_SHIFT;
185762306a36Sopenharmony_ci		break;
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	/*
186162306a36Sopenharmony_ci	 * By default mask the TC IRQ on all LLIs, it will be unmasked on
186262306a36Sopenharmony_ci	 * the last LLI item by other code.
186362306a36Sopenharmony_ci	 */
186462306a36Sopenharmony_ci	cctl |= FTDMAC020_LLI_TC_MSK;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	/*
186762306a36Sopenharmony_ci	 * Both to be incremented so leave bits FTDMAC020_LLI_SRCAD_CTL
186862306a36Sopenharmony_ci	 * and FTDMAC020_LLI_DSTAD_CTL as zero
186962306a36Sopenharmony_ci	 */
187062306a36Sopenharmony_ci	if (pl08x->vd->dualmaster)
187162306a36Sopenharmony_ci		cctl |= pl08x_select_bus(true,
187262306a36Sopenharmony_ci					 pl08x->mem_buses,
187362306a36Sopenharmony_ci					 pl08x->mem_buses);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	return cctl;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci/*
187962306a36Sopenharmony_ci * Initialize a descriptor to be used by memcpy submit
188062306a36Sopenharmony_ci */
188162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
188262306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
188362306a36Sopenharmony_ci		size_t len, unsigned long flags)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
188662306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
188762306a36Sopenharmony_ci	struct pl08x_txd *txd;
188862306a36Sopenharmony_ci	struct pl08x_sg *dsg;
188962306a36Sopenharmony_ci	int ret;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	txd = pl08x_get_txd(plchan);
189262306a36Sopenharmony_ci	if (!txd) {
189362306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
189462306a36Sopenharmony_ci			"%s no memory for descriptor\n", __func__);
189562306a36Sopenharmony_ci		return NULL;
189662306a36Sopenharmony_ci	}
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
189962306a36Sopenharmony_ci	if (!dsg) {
190062306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
190162306a36Sopenharmony_ci		return NULL;
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci	list_add_tail(&dsg->node, &txd->dsg_list);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	dsg->src_addr = src;
190662306a36Sopenharmony_ci	dsg->dst_addr = dest;
190762306a36Sopenharmony_ci	dsg->len = len;
190862306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
190962306a36Sopenharmony_ci		/* Writing CCFG zero ENABLES all interrupts */
191062306a36Sopenharmony_ci		txd->ccfg = 0;
191162306a36Sopenharmony_ci		txd->cctl = pl08x_ftdmac020_memcpy_cctl(pl08x);
191262306a36Sopenharmony_ci	} else {
191362306a36Sopenharmony_ci		txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK |
191462306a36Sopenharmony_ci			PL080_CONFIG_TC_IRQ_MASK |
191562306a36Sopenharmony_ci			PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
191662306a36Sopenharmony_ci		txd->cctl = pl08x_memcpy_cctl(pl08x);
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	ret = pl08x_fill_llis_for_desc(plchan->host, txd);
192062306a36Sopenharmony_ci	if (!ret) {
192162306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
192262306a36Sopenharmony_ci		return NULL;
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
192662306a36Sopenharmony_ci}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic struct pl08x_txd *pl08x_init_txd(
192962306a36Sopenharmony_ci		struct dma_chan *chan,
193062306a36Sopenharmony_ci		enum dma_transfer_direction direction,
193162306a36Sopenharmony_ci		dma_addr_t *slave_addr)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
193462306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
193562306a36Sopenharmony_ci	struct pl08x_txd *txd;
193662306a36Sopenharmony_ci	enum dma_slave_buswidth addr_width;
193762306a36Sopenharmony_ci	int ret, tmp;
193862306a36Sopenharmony_ci	u8 src_buses, dst_buses;
193962306a36Sopenharmony_ci	u32 maxburst, cctl;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	txd = pl08x_get_txd(plchan);
194262306a36Sopenharmony_ci	if (!txd) {
194362306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
194462306a36Sopenharmony_ci		return NULL;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	/*
194862306a36Sopenharmony_ci	 * Set up addresses, the PrimeCell configured address
194962306a36Sopenharmony_ci	 * will take precedence since this may configure the
195062306a36Sopenharmony_ci	 * channel target address dynamically at runtime.
195162306a36Sopenharmony_ci	 */
195262306a36Sopenharmony_ci	if (direction == DMA_MEM_TO_DEV) {
195362306a36Sopenharmony_ci		cctl = PL080_CONTROL_SRC_INCR;
195462306a36Sopenharmony_ci		*slave_addr = plchan->cfg.dst_addr;
195562306a36Sopenharmony_ci		addr_width = plchan->cfg.dst_addr_width;
195662306a36Sopenharmony_ci		maxburst = plchan->cfg.dst_maxburst;
195762306a36Sopenharmony_ci		src_buses = pl08x->mem_buses;
195862306a36Sopenharmony_ci		dst_buses = plchan->cd->periph_buses;
195962306a36Sopenharmony_ci	} else if (direction == DMA_DEV_TO_MEM) {
196062306a36Sopenharmony_ci		cctl = PL080_CONTROL_DST_INCR;
196162306a36Sopenharmony_ci		*slave_addr = plchan->cfg.src_addr;
196262306a36Sopenharmony_ci		addr_width = plchan->cfg.src_addr_width;
196362306a36Sopenharmony_ci		maxburst = plchan->cfg.src_maxburst;
196462306a36Sopenharmony_ci		src_buses = plchan->cd->periph_buses;
196562306a36Sopenharmony_ci		dst_buses = pl08x->mem_buses;
196662306a36Sopenharmony_ci	} else {
196762306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
196862306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
196962306a36Sopenharmony_ci			"%s direction unsupported\n", __func__);
197062306a36Sopenharmony_ci		return NULL;
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	cctl |= pl08x_get_cctl(plchan, addr_width, maxburst);
197462306a36Sopenharmony_ci	if (cctl == ~0) {
197562306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
197662306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
197762306a36Sopenharmony_ci			"DMA slave configuration botched?\n");
197862306a36Sopenharmony_ci		return NULL;
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	txd->cctl = cctl | pl08x_select_bus(false, src_buses, dst_buses);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	if (plchan->cfg.device_fc)
198462306a36Sopenharmony_ci		tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER :
198562306a36Sopenharmony_ci			PL080_FLOW_PER2MEM_PER;
198662306a36Sopenharmony_ci	else
198762306a36Sopenharmony_ci		tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER :
198862306a36Sopenharmony_ci			PL080_FLOW_PER2MEM;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK |
199162306a36Sopenharmony_ci		PL080_CONFIG_TC_IRQ_MASK |
199262306a36Sopenharmony_ci		tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	ret = pl08x_request_mux(plchan);
199562306a36Sopenharmony_ci	if (ret < 0) {
199662306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
199762306a36Sopenharmony_ci		dev_dbg(&pl08x->adev->dev,
199862306a36Sopenharmony_ci			"unable to mux for transfer on %s due to platform restrictions\n",
199962306a36Sopenharmony_ci			plchan->name);
200062306a36Sopenharmony_ci		return NULL;
200162306a36Sopenharmony_ci	}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
200462306a36Sopenharmony_ci		 plchan->signal, plchan->name);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	/* Assign the flow control signal to this channel */
200762306a36Sopenharmony_ci	if (direction == DMA_MEM_TO_DEV)
200862306a36Sopenharmony_ci		txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
200962306a36Sopenharmony_ci	else
201062306a36Sopenharmony_ci		txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	return txd;
201362306a36Sopenharmony_ci}
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_cistatic int pl08x_tx_add_sg(struct pl08x_txd *txd,
201662306a36Sopenharmony_ci			   enum dma_transfer_direction direction,
201762306a36Sopenharmony_ci			   dma_addr_t slave_addr,
201862306a36Sopenharmony_ci			   dma_addr_t buf_addr,
201962306a36Sopenharmony_ci			   unsigned int len)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	struct pl08x_sg *dsg;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
202462306a36Sopenharmony_ci	if (!dsg)
202562306a36Sopenharmony_ci		return -ENOMEM;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	list_add_tail(&dsg->node, &txd->dsg_list);
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	dsg->len = len;
203062306a36Sopenharmony_ci	if (direction == DMA_MEM_TO_DEV) {
203162306a36Sopenharmony_ci		dsg->src_addr = buf_addr;
203262306a36Sopenharmony_ci		dsg->dst_addr = slave_addr;
203362306a36Sopenharmony_ci	} else {
203462306a36Sopenharmony_ci		dsg->src_addr = slave_addr;
203562306a36Sopenharmony_ci		dsg->dst_addr = buf_addr;
203662306a36Sopenharmony_ci	}
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	return 0;
203962306a36Sopenharmony_ci}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
204262306a36Sopenharmony_ci		struct dma_chan *chan, struct scatterlist *sgl,
204362306a36Sopenharmony_ci		unsigned int sg_len, enum dma_transfer_direction direction,
204462306a36Sopenharmony_ci		unsigned long flags, void *context)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
204762306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
204862306a36Sopenharmony_ci	struct pl08x_txd *txd;
204962306a36Sopenharmony_ci	struct scatterlist *sg;
205062306a36Sopenharmony_ci	int ret, tmp;
205162306a36Sopenharmony_ci	dma_addr_t slave_addr;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
205462306a36Sopenharmony_ci			__func__, sg_dma_len(sgl), plchan->name);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	txd = pl08x_init_txd(chan, direction, &slave_addr);
205762306a36Sopenharmony_ci	if (!txd)
205862306a36Sopenharmony_ci		return NULL;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, tmp) {
206162306a36Sopenharmony_ci		ret = pl08x_tx_add_sg(txd, direction, slave_addr,
206262306a36Sopenharmony_ci				      sg_dma_address(sg),
206362306a36Sopenharmony_ci				      sg_dma_len(sg));
206462306a36Sopenharmony_ci		if (ret) {
206562306a36Sopenharmony_ci			pl08x_release_mux(plchan);
206662306a36Sopenharmony_ci			pl08x_free_txd(pl08x, txd);
206762306a36Sopenharmony_ci			dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
206862306a36Sopenharmony_ci					__func__);
206962306a36Sopenharmony_ci			return NULL;
207062306a36Sopenharmony_ci		}
207162306a36Sopenharmony_ci	}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	ret = pl08x_fill_llis_for_desc(plchan->host, txd);
207462306a36Sopenharmony_ci	if (!ret) {
207562306a36Sopenharmony_ci		pl08x_release_mux(plchan);
207662306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
207762306a36Sopenharmony_ci		return NULL;
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *pl08x_prep_dma_cyclic(
208462306a36Sopenharmony_ci		struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
208562306a36Sopenharmony_ci		size_t period_len, enum dma_transfer_direction direction,
208662306a36Sopenharmony_ci		unsigned long flags)
208762306a36Sopenharmony_ci{
208862306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
208962306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
209062306a36Sopenharmony_ci	struct pl08x_txd *txd;
209162306a36Sopenharmony_ci	int ret, tmp;
209262306a36Sopenharmony_ci	dma_addr_t slave_addr;
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev,
209562306a36Sopenharmony_ci		"%s prepare cyclic transaction of %zd/%zd bytes %s %s\n",
209662306a36Sopenharmony_ci		__func__, period_len, buf_len,
209762306a36Sopenharmony_ci		direction == DMA_MEM_TO_DEV ? "to" : "from",
209862306a36Sopenharmony_ci		plchan->name);
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	txd = pl08x_init_txd(chan, direction, &slave_addr);
210162306a36Sopenharmony_ci	if (!txd)
210262306a36Sopenharmony_ci		return NULL;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	txd->cyclic = true;
210562306a36Sopenharmony_ci	txd->cctl |= PL080_CONTROL_TC_IRQ_EN;
210662306a36Sopenharmony_ci	for (tmp = 0; tmp < buf_len; tmp += period_len) {
210762306a36Sopenharmony_ci		ret = pl08x_tx_add_sg(txd, direction, slave_addr,
210862306a36Sopenharmony_ci				      buf_addr + tmp, period_len);
210962306a36Sopenharmony_ci		if (ret) {
211062306a36Sopenharmony_ci			pl08x_release_mux(plchan);
211162306a36Sopenharmony_ci			pl08x_free_txd(pl08x, txd);
211262306a36Sopenharmony_ci			return NULL;
211362306a36Sopenharmony_ci		}
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	ret = pl08x_fill_llis_for_desc(plchan->host, txd);
211762306a36Sopenharmony_ci	if (!ret) {
211862306a36Sopenharmony_ci		pl08x_release_mux(plchan);
211962306a36Sopenharmony_ci		pl08x_free_txd(pl08x, txd);
212062306a36Sopenharmony_ci		return NULL;
212162306a36Sopenharmony_ci	}
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_cistatic int pl08x_config(struct dma_chan *chan,
212762306a36Sopenharmony_ci			struct dma_slave_config *config)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
213062306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	if (!plchan->slave)
213362306a36Sopenharmony_ci		return -EINVAL;
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	/* Reject definitely invalid configurations */
213662306a36Sopenharmony_ci	if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
213762306a36Sopenharmony_ci	    config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
213862306a36Sopenharmony_ci		return -EINVAL;
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	if (config->device_fc && pl08x->vd->pl080s) {
214162306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
214262306a36Sopenharmony_ci			"%s: PL080S does not support peripheral flow control\n",
214362306a36Sopenharmony_ci			__func__);
214462306a36Sopenharmony_ci		return -EINVAL;
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	plchan->cfg = *config;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	return 0;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_cistatic int pl08x_terminate_all(struct dma_chan *chan)
215362306a36Sopenharmony_ci{
215462306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
215562306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = plchan->host;
215662306a36Sopenharmony_ci	unsigned long flags;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	spin_lock_irqsave(&plchan->vc.lock, flags);
215962306a36Sopenharmony_ci	if (!plchan->phychan && !plchan->at) {
216062306a36Sopenharmony_ci		spin_unlock_irqrestore(&plchan->vc.lock, flags);
216162306a36Sopenharmony_ci		return 0;
216262306a36Sopenharmony_ci	}
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_IDLE;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	if (plchan->phychan) {
216762306a36Sopenharmony_ci		/*
216862306a36Sopenharmony_ci		 * Mark physical channel as free and free any slave
216962306a36Sopenharmony_ci		 * signal
217062306a36Sopenharmony_ci		 */
217162306a36Sopenharmony_ci		pl08x_phy_free(plchan);
217262306a36Sopenharmony_ci	}
217362306a36Sopenharmony_ci	/* Dequeue jobs and free LLIs */
217462306a36Sopenharmony_ci	if (plchan->at) {
217562306a36Sopenharmony_ci		vchan_terminate_vdesc(&plchan->at->vd);
217662306a36Sopenharmony_ci		plchan->at = NULL;
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci	/* Dequeue jobs not yet fired as well */
217962306a36Sopenharmony_ci	pl08x_free_txd_list(pl08x, plchan);
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	spin_unlock_irqrestore(&plchan->vc.lock, flags);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	return 0;
218462306a36Sopenharmony_ci}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_cistatic void pl08x_synchronize(struct dma_chan *chan)
218762306a36Sopenharmony_ci{
218862306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	vchan_synchronize(&plchan->vc);
219162306a36Sopenharmony_ci}
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_cistatic int pl08x_pause(struct dma_chan *chan)
219462306a36Sopenharmony_ci{
219562306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
219662306a36Sopenharmony_ci	unsigned long flags;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	/*
219962306a36Sopenharmony_ci	 * Anything succeeds on channels with no physical allocation and
220062306a36Sopenharmony_ci	 * no queued transfers.
220162306a36Sopenharmony_ci	 */
220262306a36Sopenharmony_ci	spin_lock_irqsave(&plchan->vc.lock, flags);
220362306a36Sopenharmony_ci	if (!plchan->phychan && !plchan->at) {
220462306a36Sopenharmony_ci		spin_unlock_irqrestore(&plchan->vc.lock, flags);
220562306a36Sopenharmony_ci		return 0;
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	pl08x_pause_phy_chan(plchan->phychan);
220962306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_PAUSED;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	spin_unlock_irqrestore(&plchan->vc.lock, flags);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	return 0;
221462306a36Sopenharmony_ci}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_cistatic int pl08x_resume(struct dma_chan *chan)
221762306a36Sopenharmony_ci{
221862306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
221962306a36Sopenharmony_ci	unsigned long flags;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	/*
222262306a36Sopenharmony_ci	 * Anything succeeds on channels with no physical allocation and
222362306a36Sopenharmony_ci	 * no queued transfers.
222462306a36Sopenharmony_ci	 */
222562306a36Sopenharmony_ci	spin_lock_irqsave(&plchan->vc.lock, flags);
222662306a36Sopenharmony_ci	if (!plchan->phychan && !plchan->at) {
222762306a36Sopenharmony_ci		spin_unlock_irqrestore(&plchan->vc.lock, flags);
222862306a36Sopenharmony_ci		return 0;
222962306a36Sopenharmony_ci	}
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	pl08x_resume_phy_chan(plchan->phychan);
223262306a36Sopenharmony_ci	plchan->state = PL08X_CHAN_RUNNING;
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	spin_unlock_irqrestore(&plchan->vc.lock, flags);
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	return 0;
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cibool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
224062306a36Sopenharmony_ci{
224162306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan;
224262306a36Sopenharmony_ci	char *name = chan_id;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	/* Reject channels for devices not bound to this driver */
224562306a36Sopenharmony_ci	if (chan->device->dev->driver != &pl08x_amba_driver.drv)
224662306a36Sopenharmony_ci		return false;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	plchan = to_pl08x_chan(chan);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	/* Check that the channel is not taken! */
225162306a36Sopenharmony_ci	if (!strcmp(plchan->name, name))
225262306a36Sopenharmony_ci		return true;
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci	return false;
225562306a36Sopenharmony_ci}
225662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pl08x_filter_id);
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_cistatic bool pl08x_filter_fn(struct dma_chan *chan, void *chan_id)
225962306a36Sopenharmony_ci{
226062306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	return plchan->cd == chan_id;
226362306a36Sopenharmony_ci}
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci/*
226662306a36Sopenharmony_ci * Just check that the device is there and active
226762306a36Sopenharmony_ci * TODO: turn this bit on/off depending on the number of physical channels
226862306a36Sopenharmony_ci * actually used, if it is zero... well shut it off. That will save some
226962306a36Sopenharmony_ci * power. Cut the clock at the same time.
227062306a36Sopenharmony_ci */
227162306a36Sopenharmony_cistatic void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	/* The Nomadik variant does not have the config register */
227462306a36Sopenharmony_ci	if (pl08x->vd->nomadik)
227562306a36Sopenharmony_ci		return;
227662306a36Sopenharmony_ci	/* The FTDMAC020 variant does this in another register */
227762306a36Sopenharmony_ci	if (pl08x->vd->ftdmac020) {
227862306a36Sopenharmony_ci		writel(PL080_CONFIG_ENABLE, pl08x->base + FTDMAC020_CSR);
227962306a36Sopenharmony_ci		return;
228062306a36Sopenharmony_ci	}
228162306a36Sopenharmony_ci	writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_cistatic irqreturn_t pl08x_irq(int irq, void *dev)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = dev;
228762306a36Sopenharmony_ci	u32 mask = 0, err, tc, i;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	/* check & clear - ERR & TC interrupts */
229062306a36Sopenharmony_ci	err = readl(pl08x->base + PL080_ERR_STATUS);
229162306a36Sopenharmony_ci	if (err) {
229262306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev, "%s error interrupt, register value 0x%08x\n",
229362306a36Sopenharmony_ci			__func__, err);
229462306a36Sopenharmony_ci		writel(err, pl08x->base + PL080_ERR_CLEAR);
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci	tc = readl(pl08x->base + PL080_TC_STATUS);
229762306a36Sopenharmony_ci	if (tc)
229862306a36Sopenharmony_ci		writel(tc, pl08x->base + PL080_TC_CLEAR);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (!err && !tc)
230162306a36Sopenharmony_ci		return IRQ_NONE;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	for (i = 0; i < pl08x->vd->channels; i++) {
230462306a36Sopenharmony_ci		if ((BIT(i) & err) || (BIT(i) & tc)) {
230562306a36Sopenharmony_ci			/* Locate physical channel */
230662306a36Sopenharmony_ci			struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
230762306a36Sopenharmony_ci			struct pl08x_dma_chan *plchan = phychan->serving;
230862306a36Sopenharmony_ci			struct pl08x_txd *tx;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci			if (!plchan) {
231162306a36Sopenharmony_ci				dev_err(&pl08x->adev->dev,
231262306a36Sopenharmony_ci					"%s Error TC interrupt on unused channel: 0x%08x\n",
231362306a36Sopenharmony_ci					__func__, i);
231462306a36Sopenharmony_ci				continue;
231562306a36Sopenharmony_ci			}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci			spin_lock(&plchan->vc.lock);
231862306a36Sopenharmony_ci			tx = plchan->at;
231962306a36Sopenharmony_ci			if (tx && tx->cyclic) {
232062306a36Sopenharmony_ci				vchan_cyclic_callback(&tx->vd);
232162306a36Sopenharmony_ci			} else if (tx) {
232262306a36Sopenharmony_ci				plchan->at = NULL;
232362306a36Sopenharmony_ci				/*
232462306a36Sopenharmony_ci				 * This descriptor is done, release its mux
232562306a36Sopenharmony_ci				 * reservation.
232662306a36Sopenharmony_ci				 */
232762306a36Sopenharmony_ci				pl08x_release_mux(plchan);
232862306a36Sopenharmony_ci				tx->done = true;
232962306a36Sopenharmony_ci				vchan_cookie_complete(&tx->vd);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci				/*
233262306a36Sopenharmony_ci				 * And start the next descriptor (if any),
233362306a36Sopenharmony_ci				 * otherwise free this channel.
233462306a36Sopenharmony_ci				 */
233562306a36Sopenharmony_ci				if (vchan_next_desc(&plchan->vc))
233662306a36Sopenharmony_ci					pl08x_start_next_txd(plchan);
233762306a36Sopenharmony_ci				else
233862306a36Sopenharmony_ci					pl08x_phy_free(plchan);
233962306a36Sopenharmony_ci			}
234062306a36Sopenharmony_ci			spin_unlock(&plchan->vc.lock);
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci			mask |= BIT(i);
234362306a36Sopenharmony_ci		}
234462306a36Sopenharmony_ci	}
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	return mask ? IRQ_HANDLED : IRQ_NONE;
234762306a36Sopenharmony_ci}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_cistatic void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
235062306a36Sopenharmony_ci{
235162306a36Sopenharmony_ci	chan->slave = true;
235262306a36Sopenharmony_ci	chan->name = chan->cd->bus_id;
235362306a36Sopenharmony_ci	chan->cfg.src_addr = chan->cd->addr;
235462306a36Sopenharmony_ci	chan->cfg.dst_addr = chan->cd->addr;
235562306a36Sopenharmony_ci}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci/*
235862306a36Sopenharmony_ci * Initialise the DMAC memcpy/slave channels.
235962306a36Sopenharmony_ci * Make a local wrapper to hold required data
236062306a36Sopenharmony_ci */
236162306a36Sopenharmony_cistatic int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
236262306a36Sopenharmony_ci		struct dma_device *dmadev, unsigned int channels, bool slave)
236362306a36Sopenharmony_ci{
236462306a36Sopenharmony_ci	struct pl08x_dma_chan *chan;
236562306a36Sopenharmony_ci	int i;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	INIT_LIST_HEAD(&dmadev->channels);
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	/*
237062306a36Sopenharmony_ci	 * Register as many memcpy as we have physical channels,
237162306a36Sopenharmony_ci	 * we won't always be able to use all but the code will have
237262306a36Sopenharmony_ci	 * to cope with that situation.
237362306a36Sopenharmony_ci	 */
237462306a36Sopenharmony_ci	for (i = 0; i < channels; i++) {
237562306a36Sopenharmony_ci		chan = kzalloc(sizeof(*chan), GFP_KERNEL);
237662306a36Sopenharmony_ci		if (!chan)
237762306a36Sopenharmony_ci			return -ENOMEM;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci		chan->host = pl08x;
238062306a36Sopenharmony_ci		chan->state = PL08X_CHAN_IDLE;
238162306a36Sopenharmony_ci		chan->signal = -1;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci		if (slave) {
238462306a36Sopenharmony_ci			chan->cd = &pl08x->pd->slave_channels[i];
238562306a36Sopenharmony_ci			/*
238662306a36Sopenharmony_ci			 * Some implementations have muxed signals, whereas some
238762306a36Sopenharmony_ci			 * use a mux in front of the signals and need dynamic
238862306a36Sopenharmony_ci			 * assignment of signals.
238962306a36Sopenharmony_ci			 */
239062306a36Sopenharmony_ci			chan->signal = i;
239162306a36Sopenharmony_ci			pl08x_dma_slave_init(chan);
239262306a36Sopenharmony_ci		} else {
239362306a36Sopenharmony_ci			chan->cd = kzalloc(sizeof(*chan->cd), GFP_KERNEL);
239462306a36Sopenharmony_ci			if (!chan->cd) {
239562306a36Sopenharmony_ci				kfree(chan);
239662306a36Sopenharmony_ci				return -ENOMEM;
239762306a36Sopenharmony_ci			}
239862306a36Sopenharmony_ci			chan->cd->bus_id = "memcpy";
239962306a36Sopenharmony_ci			chan->cd->periph_buses = pl08x->pd->mem_buses;
240062306a36Sopenharmony_ci			chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
240162306a36Sopenharmony_ci			if (!chan->name) {
240262306a36Sopenharmony_ci				kfree(chan->cd);
240362306a36Sopenharmony_ci				kfree(chan);
240462306a36Sopenharmony_ci				return -ENOMEM;
240562306a36Sopenharmony_ci			}
240662306a36Sopenharmony_ci		}
240762306a36Sopenharmony_ci		dev_dbg(&pl08x->adev->dev,
240862306a36Sopenharmony_ci			 "initialize virtual channel \"%s\"\n",
240962306a36Sopenharmony_ci			 chan->name);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci		chan->vc.desc_free = pl08x_desc_free;
241262306a36Sopenharmony_ci		vchan_init(&chan->vc, dmadev);
241362306a36Sopenharmony_ci	}
241462306a36Sopenharmony_ci	dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n",
241562306a36Sopenharmony_ci		 i, slave ? "slave" : "memcpy");
241662306a36Sopenharmony_ci	return i;
241762306a36Sopenharmony_ci}
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_cistatic void pl08x_free_virtual_channels(struct dma_device *dmadev)
242062306a36Sopenharmony_ci{
242162306a36Sopenharmony_ci	struct pl08x_dma_chan *chan = NULL;
242262306a36Sopenharmony_ci	struct pl08x_dma_chan *next;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	list_for_each_entry_safe(chan,
242562306a36Sopenharmony_ci				 next, &dmadev->channels, vc.chan.device_node) {
242662306a36Sopenharmony_ci		list_del(&chan->vc.chan.device_node);
242762306a36Sopenharmony_ci		kfree(chan);
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci}
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
243262306a36Sopenharmony_cistatic const char *pl08x_state_str(enum pl08x_dma_chan_state state)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	switch (state) {
243562306a36Sopenharmony_ci	case PL08X_CHAN_IDLE:
243662306a36Sopenharmony_ci		return "idle";
243762306a36Sopenharmony_ci	case PL08X_CHAN_RUNNING:
243862306a36Sopenharmony_ci		return "running";
243962306a36Sopenharmony_ci	case PL08X_CHAN_PAUSED:
244062306a36Sopenharmony_ci		return "paused";
244162306a36Sopenharmony_ci	case PL08X_CHAN_WAITING:
244262306a36Sopenharmony_ci		return "waiting";
244362306a36Sopenharmony_ci	default:
244462306a36Sopenharmony_ci		break;
244562306a36Sopenharmony_ci	}
244662306a36Sopenharmony_ci	return "UNKNOWN STATE";
244762306a36Sopenharmony_ci}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_cistatic int pl08x_debugfs_show(struct seq_file *s, void *data)
245062306a36Sopenharmony_ci{
245162306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = s->private;
245262306a36Sopenharmony_ci	struct pl08x_dma_chan *chan;
245362306a36Sopenharmony_ci	struct pl08x_phy_chan *ch;
245462306a36Sopenharmony_ci	unsigned long flags;
245562306a36Sopenharmony_ci	int i;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	seq_printf(s, "PL08x physical channels:\n");
245862306a36Sopenharmony_ci	seq_printf(s, "CHANNEL:\tUSER:\n");
245962306a36Sopenharmony_ci	seq_printf(s, "--------\t-----\n");
246062306a36Sopenharmony_ci	for (i = 0; i < pl08x->vd->channels; i++) {
246162306a36Sopenharmony_ci		struct pl08x_dma_chan *virt_chan;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci		ch = &pl08x->phy_chans[i];
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci		spin_lock_irqsave(&ch->lock, flags);
246662306a36Sopenharmony_ci		virt_chan = ch->serving;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci		seq_printf(s, "%d\t\t%s%s\n",
246962306a36Sopenharmony_ci			   ch->id,
247062306a36Sopenharmony_ci			   virt_chan ? virt_chan->name : "(none)",
247162306a36Sopenharmony_ci			   ch->locked ? " LOCKED" : "");
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, flags);
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	seq_printf(s, "\nPL08x virtual memcpy channels:\n");
247762306a36Sopenharmony_ci	seq_printf(s, "CHANNEL:\tSTATE:\n");
247862306a36Sopenharmony_ci	seq_printf(s, "--------\t------\n");
247962306a36Sopenharmony_ci	list_for_each_entry(chan, &pl08x->memcpy.channels, vc.chan.device_node) {
248062306a36Sopenharmony_ci		seq_printf(s, "%s\t\t%s\n", chan->name,
248162306a36Sopenharmony_ci			   pl08x_state_str(chan->state));
248262306a36Sopenharmony_ci	}
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	if (pl08x->has_slave) {
248562306a36Sopenharmony_ci		seq_printf(s, "\nPL08x virtual slave channels:\n");
248662306a36Sopenharmony_ci		seq_printf(s, "CHANNEL:\tSTATE:\n");
248762306a36Sopenharmony_ci		seq_printf(s, "--------\t------\n");
248862306a36Sopenharmony_ci		list_for_each_entry(chan, &pl08x->slave.channels,
248962306a36Sopenharmony_ci				    vc.chan.device_node) {
249062306a36Sopenharmony_ci			seq_printf(s, "%s\t\t%s\n", chan->name,
249162306a36Sopenharmony_ci				   pl08x_state_str(chan->state));
249262306a36Sopenharmony_ci		}
249362306a36Sopenharmony_ci	}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	return 0;
249662306a36Sopenharmony_ci}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(pl08x_debugfs);
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_cistatic void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
250162306a36Sopenharmony_ci{
250262306a36Sopenharmony_ci	/* Expose a simple debugfs interface to view all clocks */
250362306a36Sopenharmony_ci	debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO,
250462306a36Sopenharmony_ci			    NULL, pl08x, &pl08x_debugfs_fops);
250562306a36Sopenharmony_ci}
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci#else
250862306a36Sopenharmony_cistatic inline void init_pl08x_debugfs(struct pl08x_driver_data *pl08x)
250962306a36Sopenharmony_ci{
251062306a36Sopenharmony_ci}
251162306a36Sopenharmony_ci#endif
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci#ifdef CONFIG_OF
251462306a36Sopenharmony_cistatic struct dma_chan *pl08x_find_chan_id(struct pl08x_driver_data *pl08x,
251562306a36Sopenharmony_ci					 u32 id)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	struct pl08x_dma_chan *chan;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	/* Trying to get a slave channel from something with no slave support */
252062306a36Sopenharmony_ci	if (!pl08x->has_slave)
252162306a36Sopenharmony_ci		return NULL;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) {
252462306a36Sopenharmony_ci		if (chan->signal == id)
252562306a36Sopenharmony_ci			return &chan->vc.chan;
252662306a36Sopenharmony_ci	}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	return NULL;
252962306a36Sopenharmony_ci}
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_cistatic struct dma_chan *pl08x_of_xlate(struct of_phandle_args *dma_spec,
253262306a36Sopenharmony_ci				       struct of_dma *ofdma)
253362306a36Sopenharmony_ci{
253462306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x = ofdma->of_dma_data;
253562306a36Sopenharmony_ci	struct dma_chan *dma_chan;
253662306a36Sopenharmony_ci	struct pl08x_dma_chan *plchan;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	if (!pl08x)
253962306a36Sopenharmony_ci		return NULL;
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	if (dma_spec->args_count != 2) {
254262306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
254362306a36Sopenharmony_ci			"DMA channel translation requires two cells\n");
254462306a36Sopenharmony_ci		return NULL;
254562306a36Sopenharmony_ci	}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	dma_chan = pl08x_find_chan_id(pl08x, dma_spec->args[0]);
254862306a36Sopenharmony_ci	if (!dma_chan) {
254962306a36Sopenharmony_ci		dev_err(&pl08x->adev->dev,
255062306a36Sopenharmony_ci			"DMA slave channel not found\n");
255162306a36Sopenharmony_ci		return NULL;
255262306a36Sopenharmony_ci	}
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	plchan = to_pl08x_chan(dma_chan);
255562306a36Sopenharmony_ci	dev_dbg(&pl08x->adev->dev,
255662306a36Sopenharmony_ci		"translated channel for signal %d\n",
255762306a36Sopenharmony_ci		dma_spec->args[0]);
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	/* Augment channel data for applicable AHB buses */
256062306a36Sopenharmony_ci	plchan->cd->periph_buses = dma_spec->args[1];
256162306a36Sopenharmony_ci	return dma_get_slave_channel(dma_chan);
256262306a36Sopenharmony_ci}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_cistatic int pl08x_of_probe(struct amba_device *adev,
256562306a36Sopenharmony_ci			  struct pl08x_driver_data *pl08x,
256662306a36Sopenharmony_ci			  struct device_node *np)
256762306a36Sopenharmony_ci{
256862306a36Sopenharmony_ci	struct pl08x_platform_data *pd;
256962306a36Sopenharmony_ci	struct pl08x_channel_data *chanp = NULL;
257062306a36Sopenharmony_ci	u32 val;
257162306a36Sopenharmony_ci	int ret;
257262306a36Sopenharmony_ci	int i;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	pd = devm_kzalloc(&adev->dev, sizeof(*pd), GFP_KERNEL);
257562306a36Sopenharmony_ci	if (!pd)
257662306a36Sopenharmony_ci		return -ENOMEM;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	/* Eligible bus masters for fetching LLIs */
257962306a36Sopenharmony_ci	if (of_property_read_bool(np, "lli-bus-interface-ahb1"))
258062306a36Sopenharmony_ci		pd->lli_buses |= PL08X_AHB1;
258162306a36Sopenharmony_ci	if (of_property_read_bool(np, "lli-bus-interface-ahb2"))
258262306a36Sopenharmony_ci		pd->lli_buses |= PL08X_AHB2;
258362306a36Sopenharmony_ci	if (!pd->lli_buses) {
258462306a36Sopenharmony_ci		dev_info(&adev->dev, "no bus masters for LLIs stated, assume all\n");
258562306a36Sopenharmony_ci		pd->lli_buses |= PL08X_AHB1 | PL08X_AHB2;
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* Eligible bus masters for memory access */
258962306a36Sopenharmony_ci	if (of_property_read_bool(np, "mem-bus-interface-ahb1"))
259062306a36Sopenharmony_ci		pd->mem_buses |= PL08X_AHB1;
259162306a36Sopenharmony_ci	if (of_property_read_bool(np, "mem-bus-interface-ahb2"))
259262306a36Sopenharmony_ci		pd->mem_buses |= PL08X_AHB2;
259362306a36Sopenharmony_ci	if (!pd->mem_buses) {
259462306a36Sopenharmony_ci		dev_info(&adev->dev, "no bus masters for memory stated, assume all\n");
259562306a36Sopenharmony_ci		pd->mem_buses |= PL08X_AHB1 | PL08X_AHB2;
259662306a36Sopenharmony_ci	}
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	/* Parse the memcpy channel properties */
259962306a36Sopenharmony_ci	ret = of_property_read_u32(np, "memcpy-burst-size", &val);
260062306a36Sopenharmony_ci	if (ret) {
260162306a36Sopenharmony_ci		dev_info(&adev->dev, "no memcpy burst size specified, using 1 byte\n");
260262306a36Sopenharmony_ci		val = 1;
260362306a36Sopenharmony_ci	}
260462306a36Sopenharmony_ci	switch (val) {
260562306a36Sopenharmony_ci	default:
260662306a36Sopenharmony_ci		dev_err(&adev->dev, "illegal burst size for memcpy, set to 1\n");
260762306a36Sopenharmony_ci		fallthrough;
260862306a36Sopenharmony_ci	case 1:
260962306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_1;
261062306a36Sopenharmony_ci		break;
261162306a36Sopenharmony_ci	case 4:
261262306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_4;
261362306a36Sopenharmony_ci		break;
261462306a36Sopenharmony_ci	case 8:
261562306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_8;
261662306a36Sopenharmony_ci		break;
261762306a36Sopenharmony_ci	case 16:
261862306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_16;
261962306a36Sopenharmony_ci		break;
262062306a36Sopenharmony_ci	case 32:
262162306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_32;
262262306a36Sopenharmony_ci		break;
262362306a36Sopenharmony_ci	case 64:
262462306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_64;
262562306a36Sopenharmony_ci		break;
262662306a36Sopenharmony_ci	case 128:
262762306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_128;
262862306a36Sopenharmony_ci		break;
262962306a36Sopenharmony_ci	case 256:
263062306a36Sopenharmony_ci		pd->memcpy_burst_size = PL08X_BURST_SZ_256;
263162306a36Sopenharmony_ci		break;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	ret = of_property_read_u32(np, "memcpy-bus-width", &val);
263562306a36Sopenharmony_ci	if (ret) {
263662306a36Sopenharmony_ci		dev_info(&adev->dev, "no memcpy bus width specified, using 8 bits\n");
263762306a36Sopenharmony_ci		val = 8;
263862306a36Sopenharmony_ci	}
263962306a36Sopenharmony_ci	switch (val) {
264062306a36Sopenharmony_ci	default:
264162306a36Sopenharmony_ci		dev_err(&adev->dev, "illegal bus width for memcpy, set to 8 bits\n");
264262306a36Sopenharmony_ci		fallthrough;
264362306a36Sopenharmony_ci	case 8:
264462306a36Sopenharmony_ci		pd->memcpy_bus_width = PL08X_BUS_WIDTH_8_BITS;
264562306a36Sopenharmony_ci		break;
264662306a36Sopenharmony_ci	case 16:
264762306a36Sopenharmony_ci		pd->memcpy_bus_width = PL08X_BUS_WIDTH_16_BITS;
264862306a36Sopenharmony_ci		break;
264962306a36Sopenharmony_ci	case 32:
265062306a36Sopenharmony_ci		pd->memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS;
265162306a36Sopenharmony_ci		break;
265262306a36Sopenharmony_ci	}
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	/*
265562306a36Sopenharmony_ci	 * Allocate channel data for all possible slave channels (one
265662306a36Sopenharmony_ci	 * for each possible signal), channels will then be allocated
265762306a36Sopenharmony_ci	 * for a device and have it's AHB interfaces set up at
265862306a36Sopenharmony_ci	 * translation time.
265962306a36Sopenharmony_ci	 */
266062306a36Sopenharmony_ci	if (pl08x->vd->signals) {
266162306a36Sopenharmony_ci		chanp = devm_kcalloc(&adev->dev,
266262306a36Sopenharmony_ci				     pl08x->vd->signals,
266362306a36Sopenharmony_ci				     sizeof(struct pl08x_channel_data),
266462306a36Sopenharmony_ci				     GFP_KERNEL);
266562306a36Sopenharmony_ci		if (!chanp)
266662306a36Sopenharmony_ci			return -ENOMEM;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci		pd->slave_channels = chanp;
266962306a36Sopenharmony_ci		for (i = 0; i < pl08x->vd->signals; i++) {
267062306a36Sopenharmony_ci			/*
267162306a36Sopenharmony_ci			 * chanp->periph_buses will be assigned at translation
267262306a36Sopenharmony_ci			 */
267362306a36Sopenharmony_ci			chanp->bus_id = kasprintf(GFP_KERNEL, "slave%d", i);
267462306a36Sopenharmony_ci			chanp++;
267562306a36Sopenharmony_ci		}
267662306a36Sopenharmony_ci		pd->num_slave_channels = pl08x->vd->signals;
267762306a36Sopenharmony_ci	}
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	pl08x->pd = pd;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	return of_dma_controller_register(adev->dev.of_node, pl08x_of_xlate,
268262306a36Sopenharmony_ci					  pl08x);
268362306a36Sopenharmony_ci}
268462306a36Sopenharmony_ci#else
268562306a36Sopenharmony_cistatic inline int pl08x_of_probe(struct amba_device *adev,
268662306a36Sopenharmony_ci				 struct pl08x_driver_data *pl08x,
268762306a36Sopenharmony_ci				 struct device_node *np)
268862306a36Sopenharmony_ci{
268962306a36Sopenharmony_ci	return -EINVAL;
269062306a36Sopenharmony_ci}
269162306a36Sopenharmony_ci#endif
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_cistatic int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
269462306a36Sopenharmony_ci{
269562306a36Sopenharmony_ci	struct pl08x_driver_data *pl08x;
269662306a36Sopenharmony_ci	struct vendor_data *vd = id->data;
269762306a36Sopenharmony_ci	struct device_node *np = adev->dev.of_node;
269862306a36Sopenharmony_ci	u32 tsfr_size;
269962306a36Sopenharmony_ci	int ret = 0;
270062306a36Sopenharmony_ci	int i;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	ret = amba_request_regions(adev, NULL);
270362306a36Sopenharmony_ci	if (ret)
270462306a36Sopenharmony_ci		return ret;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	/* Ensure that we can do DMA */
270762306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32));
270862306a36Sopenharmony_ci	if (ret)
270962306a36Sopenharmony_ci		goto out_no_pl08x;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	/* Create the driver state holder */
271262306a36Sopenharmony_ci	pl08x = kzalloc(sizeof(*pl08x), GFP_KERNEL);
271362306a36Sopenharmony_ci	if (!pl08x) {
271462306a36Sopenharmony_ci		ret = -ENOMEM;
271562306a36Sopenharmony_ci		goto out_no_pl08x;
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	/* Assign useful pointers to the driver state */
271962306a36Sopenharmony_ci	pl08x->adev = adev;
272062306a36Sopenharmony_ci	pl08x->vd = vd;
272162306a36Sopenharmony_ci
272262306a36Sopenharmony_ci	pl08x->base = ioremap(adev->res.start, resource_size(&adev->res));
272362306a36Sopenharmony_ci	if (!pl08x->base) {
272462306a36Sopenharmony_ci		ret = -ENOMEM;
272562306a36Sopenharmony_ci		goto out_no_ioremap;
272662306a36Sopenharmony_ci	}
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	if (vd->ftdmac020) {
272962306a36Sopenharmony_ci		u32 val;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci		val = readl(pl08x->base + FTDMAC020_REVISION);
273262306a36Sopenharmony_ci		dev_info(&pl08x->adev->dev, "FTDMAC020 %d.%d rel %d\n",
273362306a36Sopenharmony_ci			 (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
273462306a36Sopenharmony_ci		val = readl(pl08x->base + FTDMAC020_FEATURE);
273562306a36Sopenharmony_ci		dev_info(&pl08x->adev->dev, "FTDMAC020 %d channels, "
273662306a36Sopenharmony_ci			 "%s built-in bridge, %s, %s linked lists\n",
273762306a36Sopenharmony_ci			 (val >> 12) & 0x0f,
273862306a36Sopenharmony_ci			 (val & BIT(10)) ? "no" : "has",
273962306a36Sopenharmony_ci			 (val & BIT(9)) ? "AHB0 and AHB1" : "AHB0",
274062306a36Sopenharmony_ci			 (val & BIT(8)) ? "supports" : "does not support");
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci		/* Vendor data from feature register */
274362306a36Sopenharmony_ci		if (!(val & BIT(8)))
274462306a36Sopenharmony_ci			dev_warn(&pl08x->adev->dev,
274562306a36Sopenharmony_ci				 "linked lists not supported, required\n");
274662306a36Sopenharmony_ci		vd->channels = (val >> 12) & 0x0f;
274762306a36Sopenharmony_ci		vd->dualmaster = !!(val & BIT(9));
274862306a36Sopenharmony_ci	}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	/* Initialize memcpy engine */
275162306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
275262306a36Sopenharmony_ci	pl08x->memcpy.dev = &adev->dev;
275362306a36Sopenharmony_ci	pl08x->memcpy.device_free_chan_resources = pl08x_free_chan_resources;
275462306a36Sopenharmony_ci	pl08x->memcpy.device_prep_dma_memcpy = pl08x_prep_dma_memcpy;
275562306a36Sopenharmony_ci	pl08x->memcpy.device_tx_status = pl08x_dma_tx_status;
275662306a36Sopenharmony_ci	pl08x->memcpy.device_issue_pending = pl08x_issue_pending;
275762306a36Sopenharmony_ci	pl08x->memcpy.device_config = pl08x_config;
275862306a36Sopenharmony_ci	pl08x->memcpy.device_pause = pl08x_pause;
275962306a36Sopenharmony_ci	pl08x->memcpy.device_resume = pl08x_resume;
276062306a36Sopenharmony_ci	pl08x->memcpy.device_terminate_all = pl08x_terminate_all;
276162306a36Sopenharmony_ci	pl08x->memcpy.device_synchronize = pl08x_synchronize;
276262306a36Sopenharmony_ci	pl08x->memcpy.src_addr_widths = PL80X_DMA_BUSWIDTHS;
276362306a36Sopenharmony_ci	pl08x->memcpy.dst_addr_widths = PL80X_DMA_BUSWIDTHS;
276462306a36Sopenharmony_ci	pl08x->memcpy.directions = BIT(DMA_MEM_TO_MEM);
276562306a36Sopenharmony_ci	pl08x->memcpy.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
276662306a36Sopenharmony_ci	if (vd->ftdmac020)
276762306a36Sopenharmony_ci		pl08x->memcpy.copy_align = DMAENGINE_ALIGN_4_BYTES;
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	/*
277162306a36Sopenharmony_ci	 * Initialize slave engine, if the block has no signals, that means
277262306a36Sopenharmony_ci	 * we have no slave support.
277362306a36Sopenharmony_ci	 */
277462306a36Sopenharmony_ci	if (vd->signals) {
277562306a36Sopenharmony_ci		pl08x->has_slave = true;
277662306a36Sopenharmony_ci		dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask);
277762306a36Sopenharmony_ci		dma_cap_set(DMA_CYCLIC, pl08x->slave.cap_mask);
277862306a36Sopenharmony_ci		pl08x->slave.dev = &adev->dev;
277962306a36Sopenharmony_ci		pl08x->slave.device_free_chan_resources =
278062306a36Sopenharmony_ci			pl08x_free_chan_resources;
278162306a36Sopenharmony_ci		pl08x->slave.device_tx_status = pl08x_dma_tx_status;
278262306a36Sopenharmony_ci		pl08x->slave.device_issue_pending = pl08x_issue_pending;
278362306a36Sopenharmony_ci		pl08x->slave.device_prep_slave_sg = pl08x_prep_slave_sg;
278462306a36Sopenharmony_ci		pl08x->slave.device_prep_dma_cyclic = pl08x_prep_dma_cyclic;
278562306a36Sopenharmony_ci		pl08x->slave.device_config = pl08x_config;
278662306a36Sopenharmony_ci		pl08x->slave.device_pause = pl08x_pause;
278762306a36Sopenharmony_ci		pl08x->slave.device_resume = pl08x_resume;
278862306a36Sopenharmony_ci		pl08x->slave.device_terminate_all = pl08x_terminate_all;
278962306a36Sopenharmony_ci		pl08x->slave.device_synchronize = pl08x_synchronize;
279062306a36Sopenharmony_ci		pl08x->slave.src_addr_widths = PL80X_DMA_BUSWIDTHS;
279162306a36Sopenharmony_ci		pl08x->slave.dst_addr_widths = PL80X_DMA_BUSWIDTHS;
279262306a36Sopenharmony_ci		pl08x->slave.directions =
279362306a36Sopenharmony_ci			BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
279462306a36Sopenharmony_ci		pl08x->slave.residue_granularity =
279562306a36Sopenharmony_ci			DMA_RESIDUE_GRANULARITY_SEGMENT;
279662306a36Sopenharmony_ci	}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_ci	/* Get the platform data */
279962306a36Sopenharmony_ci	pl08x->pd = dev_get_platdata(&adev->dev);
280062306a36Sopenharmony_ci	if (!pl08x->pd) {
280162306a36Sopenharmony_ci		if (np) {
280262306a36Sopenharmony_ci			ret = pl08x_of_probe(adev, pl08x, np);
280362306a36Sopenharmony_ci			if (ret)
280462306a36Sopenharmony_ci				goto out_no_platdata;
280562306a36Sopenharmony_ci		} else {
280662306a36Sopenharmony_ci			dev_err(&adev->dev, "no platform data supplied\n");
280762306a36Sopenharmony_ci			ret = -EINVAL;
280862306a36Sopenharmony_ci			goto out_no_platdata;
280962306a36Sopenharmony_ci		}
281062306a36Sopenharmony_ci	} else {
281162306a36Sopenharmony_ci		pl08x->slave.filter.map = pl08x->pd->slave_map;
281262306a36Sopenharmony_ci		pl08x->slave.filter.mapcnt = pl08x->pd->slave_map_len;
281362306a36Sopenharmony_ci		pl08x->slave.filter.fn = pl08x_filter_fn;
281462306a36Sopenharmony_ci	}
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	/* By default, AHB1 only.  If dualmaster, from platform */
281762306a36Sopenharmony_ci	pl08x->lli_buses = PL08X_AHB1;
281862306a36Sopenharmony_ci	pl08x->mem_buses = PL08X_AHB1;
281962306a36Sopenharmony_ci	if (pl08x->vd->dualmaster) {
282062306a36Sopenharmony_ci		pl08x->lli_buses = pl08x->pd->lli_buses;
282162306a36Sopenharmony_ci		pl08x->mem_buses = pl08x->pd->mem_buses;
282262306a36Sopenharmony_ci	}
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	if (vd->pl080s)
282562306a36Sopenharmony_ci		pl08x->lli_words = PL080S_LLI_WORDS;
282662306a36Sopenharmony_ci	else
282762306a36Sopenharmony_ci		pl08x->lli_words = PL080_LLI_WORDS;
282862306a36Sopenharmony_ci	tsfr_size = MAX_NUM_TSFR_LLIS * pl08x->lli_words * sizeof(u32);
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	/* A DMA memory pool for LLIs, align on 1-byte boundary */
283162306a36Sopenharmony_ci	pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev,
283262306a36Sopenharmony_ci						tsfr_size, PL08X_ALIGN, 0);
283362306a36Sopenharmony_ci	if (!pl08x->pool) {
283462306a36Sopenharmony_ci		ret = -ENOMEM;
283562306a36Sopenharmony_ci		goto out_no_lli_pool;
283662306a36Sopenharmony_ci	}
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	/* Turn on the PL08x */
283962306a36Sopenharmony_ci	pl08x_ensure_on(pl08x);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci	/* Clear any pending interrupts */
284262306a36Sopenharmony_ci	if (vd->ftdmac020)
284362306a36Sopenharmony_ci		/* This variant has error IRQs in bits 16-19 */
284462306a36Sopenharmony_ci		writel(0x0000FFFF, pl08x->base + PL080_ERR_CLEAR);
284562306a36Sopenharmony_ci	else
284662306a36Sopenharmony_ci		writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR);
284762306a36Sopenharmony_ci	writel(0x000000FF, pl08x->base + PL080_TC_CLEAR);
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	/* Attach the interrupt handler */
285062306a36Sopenharmony_ci	ret = request_irq(adev->irq[0], pl08x_irq, 0, DRIVER_NAME, pl08x);
285162306a36Sopenharmony_ci	if (ret) {
285262306a36Sopenharmony_ci		dev_err(&adev->dev, "%s failed to request interrupt %d\n",
285362306a36Sopenharmony_ci			__func__, adev->irq[0]);
285462306a36Sopenharmony_ci		goto out_no_irq;
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_ci	/* Initialize physical channels */
285862306a36Sopenharmony_ci	pl08x->phy_chans = kzalloc((vd->channels * sizeof(*pl08x->phy_chans)),
285962306a36Sopenharmony_ci			GFP_KERNEL);
286062306a36Sopenharmony_ci	if (!pl08x->phy_chans) {
286162306a36Sopenharmony_ci		ret = -ENOMEM;
286262306a36Sopenharmony_ci		goto out_no_phychans;
286362306a36Sopenharmony_ci	}
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	for (i = 0; i < vd->channels; i++) {
286662306a36Sopenharmony_ci		struct pl08x_phy_chan *ch = &pl08x->phy_chans[i];
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci		ch->id = i;
286962306a36Sopenharmony_ci		ch->base = pl08x->base + PL080_Cx_BASE(i);
287062306a36Sopenharmony_ci		if (vd->ftdmac020) {
287162306a36Sopenharmony_ci			/* FTDMA020 has a special channel busy register */
287262306a36Sopenharmony_ci			ch->reg_busy = ch->base + FTDMAC020_CH_BUSY;
287362306a36Sopenharmony_ci			ch->reg_config = ch->base + FTDMAC020_CH_CFG;
287462306a36Sopenharmony_ci			ch->reg_control = ch->base + FTDMAC020_CH_CSR;
287562306a36Sopenharmony_ci			ch->reg_src = ch->base + FTDMAC020_CH_SRC_ADDR;
287662306a36Sopenharmony_ci			ch->reg_dst = ch->base + FTDMAC020_CH_DST_ADDR;
287762306a36Sopenharmony_ci			ch->reg_lli = ch->base + FTDMAC020_CH_LLP;
287862306a36Sopenharmony_ci			ch->ftdmac020 = true;
287962306a36Sopenharmony_ci		} else {
288062306a36Sopenharmony_ci			ch->reg_config = ch->base + vd->config_offset;
288162306a36Sopenharmony_ci			ch->reg_control = ch->base + PL080_CH_CONTROL;
288262306a36Sopenharmony_ci			ch->reg_src = ch->base + PL080_CH_SRC_ADDR;
288362306a36Sopenharmony_ci			ch->reg_dst = ch->base + PL080_CH_DST_ADDR;
288462306a36Sopenharmony_ci			ch->reg_lli = ch->base + PL080_CH_LLI;
288562306a36Sopenharmony_ci		}
288662306a36Sopenharmony_ci		if (vd->pl080s)
288762306a36Sopenharmony_ci			ch->pl080s = true;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci		spin_lock_init(&ch->lock);
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci		/*
289262306a36Sopenharmony_ci		 * Nomadik variants can have channels that are locked
289362306a36Sopenharmony_ci		 * down for the secure world only. Lock up these channels
289462306a36Sopenharmony_ci		 * by perpetually serving a dummy virtual channel.
289562306a36Sopenharmony_ci		 */
289662306a36Sopenharmony_ci		if (vd->nomadik) {
289762306a36Sopenharmony_ci			u32 val;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci			val = readl(ch->reg_config);
290062306a36Sopenharmony_ci			if (val & (PL080N_CONFIG_ITPROT | PL080N_CONFIG_SECPROT)) {
290162306a36Sopenharmony_ci				dev_info(&adev->dev, "physical channel %d reserved for secure access only\n", i);
290262306a36Sopenharmony_ci				ch->locked = true;
290362306a36Sopenharmony_ci			}
290462306a36Sopenharmony_ci		}
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci		dev_dbg(&adev->dev, "physical channel %d is %s\n",
290762306a36Sopenharmony_ci			i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE");
290862306a36Sopenharmony_ci	}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	/* Register as many memcpy channels as there are physical channels */
291162306a36Sopenharmony_ci	ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->memcpy,
291262306a36Sopenharmony_ci					      pl08x->vd->channels, false);
291362306a36Sopenharmony_ci	if (ret <= 0) {
291462306a36Sopenharmony_ci		dev_warn(&pl08x->adev->dev,
291562306a36Sopenharmony_ci			 "%s failed to enumerate memcpy channels - %d\n",
291662306a36Sopenharmony_ci			 __func__, ret);
291762306a36Sopenharmony_ci		goto out_no_memcpy;
291862306a36Sopenharmony_ci	}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	/* Register slave channels */
292162306a36Sopenharmony_ci	if (pl08x->has_slave) {
292262306a36Sopenharmony_ci		ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave,
292362306a36Sopenharmony_ci					pl08x->pd->num_slave_channels, true);
292462306a36Sopenharmony_ci		if (ret < 0) {
292562306a36Sopenharmony_ci			dev_warn(&pl08x->adev->dev,
292662306a36Sopenharmony_ci				 "%s failed to enumerate slave channels - %d\n",
292762306a36Sopenharmony_ci				 __func__, ret);
292862306a36Sopenharmony_ci			goto out_no_slave;
292962306a36Sopenharmony_ci		}
293062306a36Sopenharmony_ci	}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	ret = dma_async_device_register(&pl08x->memcpy);
293362306a36Sopenharmony_ci	if (ret) {
293462306a36Sopenharmony_ci		dev_warn(&pl08x->adev->dev,
293562306a36Sopenharmony_ci			"%s failed to register memcpy as an async device - %d\n",
293662306a36Sopenharmony_ci			__func__, ret);
293762306a36Sopenharmony_ci		goto out_no_memcpy_reg;
293862306a36Sopenharmony_ci	}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	if (pl08x->has_slave) {
294162306a36Sopenharmony_ci		ret = dma_async_device_register(&pl08x->slave);
294262306a36Sopenharmony_ci		if (ret) {
294362306a36Sopenharmony_ci			dev_warn(&pl08x->adev->dev,
294462306a36Sopenharmony_ci			"%s failed to register slave as an async device - %d\n",
294562306a36Sopenharmony_ci			__func__, ret);
294662306a36Sopenharmony_ci			goto out_no_slave_reg;
294762306a36Sopenharmony_ci		}
294862306a36Sopenharmony_ci	}
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	amba_set_drvdata(adev, pl08x);
295162306a36Sopenharmony_ci	init_pl08x_debugfs(pl08x);
295262306a36Sopenharmony_ci	dev_info(&pl08x->adev->dev, "DMA: PL%03x%s rev%u at 0x%08llx irq %d\n",
295362306a36Sopenharmony_ci		 amba_part(adev), pl08x->vd->pl080s ? "s" : "", amba_rev(adev),
295462306a36Sopenharmony_ci		 (unsigned long long)adev->res.start, adev->irq[0]);
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	return 0;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ciout_no_slave_reg:
295962306a36Sopenharmony_ci	dma_async_device_unregister(&pl08x->memcpy);
296062306a36Sopenharmony_ciout_no_memcpy_reg:
296162306a36Sopenharmony_ci	if (pl08x->has_slave)
296262306a36Sopenharmony_ci		pl08x_free_virtual_channels(&pl08x->slave);
296362306a36Sopenharmony_ciout_no_slave:
296462306a36Sopenharmony_ci	pl08x_free_virtual_channels(&pl08x->memcpy);
296562306a36Sopenharmony_ciout_no_memcpy:
296662306a36Sopenharmony_ci	kfree(pl08x->phy_chans);
296762306a36Sopenharmony_ciout_no_phychans:
296862306a36Sopenharmony_ci	free_irq(adev->irq[0], pl08x);
296962306a36Sopenharmony_ciout_no_irq:
297062306a36Sopenharmony_ci	dma_pool_destroy(pl08x->pool);
297162306a36Sopenharmony_ciout_no_lli_pool:
297262306a36Sopenharmony_ciout_no_platdata:
297362306a36Sopenharmony_ci	iounmap(pl08x->base);
297462306a36Sopenharmony_ciout_no_ioremap:
297562306a36Sopenharmony_ci	kfree(pl08x);
297662306a36Sopenharmony_ciout_no_pl08x:
297762306a36Sopenharmony_ci	amba_release_regions(adev);
297862306a36Sopenharmony_ci	return ret;
297962306a36Sopenharmony_ci}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci/* PL080 has 8 channels and the PL080 have just 2 */
298262306a36Sopenharmony_cistatic struct vendor_data vendor_pl080 = {
298362306a36Sopenharmony_ci	.config_offset = PL080_CH_CONFIG,
298462306a36Sopenharmony_ci	.channels = 8,
298562306a36Sopenharmony_ci	.signals = 16,
298662306a36Sopenharmony_ci	.dualmaster = true,
298762306a36Sopenharmony_ci	.max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK,
298862306a36Sopenharmony_ci};
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_cistatic struct vendor_data vendor_nomadik = {
299162306a36Sopenharmony_ci	.config_offset = PL080_CH_CONFIG,
299262306a36Sopenharmony_ci	.channels = 8,
299362306a36Sopenharmony_ci	.signals = 32,
299462306a36Sopenharmony_ci	.dualmaster = true,
299562306a36Sopenharmony_ci	.nomadik = true,
299662306a36Sopenharmony_ci	.max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK,
299762306a36Sopenharmony_ci};
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_cistatic struct vendor_data vendor_pl080s = {
300062306a36Sopenharmony_ci	.config_offset = PL080S_CH_CONFIG,
300162306a36Sopenharmony_ci	.channels = 8,
300262306a36Sopenharmony_ci	.signals = 32,
300362306a36Sopenharmony_ci	.pl080s = true,
300462306a36Sopenharmony_ci	.max_transfer_size = PL080S_CONTROL_TRANSFER_SIZE_MASK,
300562306a36Sopenharmony_ci};
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_cistatic struct vendor_data vendor_pl081 = {
300862306a36Sopenharmony_ci	.config_offset = PL080_CH_CONFIG,
300962306a36Sopenharmony_ci	.channels = 2,
301062306a36Sopenharmony_ci	.signals = 16,
301162306a36Sopenharmony_ci	.dualmaster = false,
301262306a36Sopenharmony_ci	.max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK,
301362306a36Sopenharmony_ci};
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_cistatic struct vendor_data vendor_ftdmac020 = {
301662306a36Sopenharmony_ci	.config_offset = PL080_CH_CONFIG,
301762306a36Sopenharmony_ci	.ftdmac020 = true,
301862306a36Sopenharmony_ci	.max_transfer_size = PL080_CONTROL_TRANSFER_SIZE_MASK,
301962306a36Sopenharmony_ci};
302062306a36Sopenharmony_ci
302162306a36Sopenharmony_cistatic const struct amba_id pl08x_ids[] = {
302262306a36Sopenharmony_ci	/* Samsung PL080S variant */
302362306a36Sopenharmony_ci	{
302462306a36Sopenharmony_ci		.id	= 0x0a141080,
302562306a36Sopenharmony_ci		.mask	= 0xffffffff,
302662306a36Sopenharmony_ci		.data	= &vendor_pl080s,
302762306a36Sopenharmony_ci	},
302862306a36Sopenharmony_ci	/* PL080 */
302962306a36Sopenharmony_ci	{
303062306a36Sopenharmony_ci		.id	= 0x00041080,
303162306a36Sopenharmony_ci		.mask	= 0x000fffff,
303262306a36Sopenharmony_ci		.data	= &vendor_pl080,
303362306a36Sopenharmony_ci	},
303462306a36Sopenharmony_ci	/* PL081 */
303562306a36Sopenharmony_ci	{
303662306a36Sopenharmony_ci		.id	= 0x00041081,
303762306a36Sopenharmony_ci		.mask	= 0x000fffff,
303862306a36Sopenharmony_ci		.data	= &vendor_pl081,
303962306a36Sopenharmony_ci	},
304062306a36Sopenharmony_ci	/* Nomadik 8815 PL080 variant */
304162306a36Sopenharmony_ci	{
304262306a36Sopenharmony_ci		.id	= 0x00280080,
304362306a36Sopenharmony_ci		.mask	= 0x00ffffff,
304462306a36Sopenharmony_ci		.data	= &vendor_nomadik,
304562306a36Sopenharmony_ci	},
304662306a36Sopenharmony_ci	/* Faraday Technology FTDMAC020 */
304762306a36Sopenharmony_ci	{
304862306a36Sopenharmony_ci		.id	= 0x0003b080,
304962306a36Sopenharmony_ci		.mask	= 0x000fffff,
305062306a36Sopenharmony_ci		.data	= &vendor_ftdmac020,
305162306a36Sopenharmony_ci	},
305262306a36Sopenharmony_ci	{ 0, 0 },
305362306a36Sopenharmony_ci};
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl08x_ids);
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_cistatic struct amba_driver pl08x_amba_driver = {
305862306a36Sopenharmony_ci	.drv.name	= DRIVER_NAME,
305962306a36Sopenharmony_ci	.id_table	= pl08x_ids,
306062306a36Sopenharmony_ci	.probe		= pl08x_probe,
306162306a36Sopenharmony_ci};
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_cistatic int __init pl08x_init(void)
306462306a36Sopenharmony_ci{
306562306a36Sopenharmony_ci	int retval;
306662306a36Sopenharmony_ci	retval = amba_driver_register(&pl08x_amba_driver);
306762306a36Sopenharmony_ci	if (retval)
306862306a36Sopenharmony_ci		printk(KERN_WARNING DRIVER_NAME
306962306a36Sopenharmony_ci		       "failed to register as an AMBA device (%d)\n",
307062306a36Sopenharmony_ci		       retval);
307162306a36Sopenharmony_ci	return retval;
307262306a36Sopenharmony_ci}
307362306a36Sopenharmony_cisubsys_initcall(pl08x_init);
3074