12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b096c137SEmilio López /*
3b096c137SEmilio López * Copyright (C) 2014 Emilio López
4b096c137SEmilio López * Emilio López <emilio@elopez.com.ar>
5b096c137SEmilio López */
6b096c137SEmilio López
7b096c137SEmilio López #include <linux/bitmap.h>
8b096c137SEmilio López #include <linux/bitops.h>
9b096c137SEmilio López #include <linux/clk.h>
10a94a098aSSamuel Holland #include <linux/dma-mapping.h>
11b096c137SEmilio López #include <linux/dmaengine.h>
12b096c137SEmilio López #include <linux/dmapool.h>
13b096c137SEmilio López #include <linux/interrupt.h>
14b096c137SEmilio López #include <linux/module.h>
15b096c137SEmilio López #include <linux/of_dma.h>
16b096c137SEmilio López #include <linux/platform_device.h>
17b096c137SEmilio López #include <linux/slab.h>
18b096c137SEmilio López #include <linux/spinlock.h>
19b096c137SEmilio López
20b096c137SEmilio López #include "virt-dma.h"
21b096c137SEmilio López
22b096c137SEmilio López /** Common macros to normal and dedicated DMA registers **/
23b096c137SEmilio López
24b096c137SEmilio López #define SUN4I_DMA_CFG_LOADING BIT(31)
25b096c137SEmilio López #define SUN4I_DMA_CFG_DST_DATA_WIDTH(width) ((width) << 25)
26b096c137SEmilio López #define SUN4I_DMA_CFG_DST_BURST_LENGTH(len) ((len) << 23)
27b096c137SEmilio López #define SUN4I_DMA_CFG_DST_ADDR_MODE(mode) ((mode) << 21)
28b096c137SEmilio López #define SUN4I_DMA_CFG_DST_DRQ_TYPE(type) ((type) << 16)
29b096c137SEmilio López #define SUN4I_DMA_CFG_SRC_DATA_WIDTH(width) ((width) << 9)
30b096c137SEmilio López #define SUN4I_DMA_CFG_SRC_BURST_LENGTH(len) ((len) << 7)
31b096c137SEmilio López #define SUN4I_DMA_CFG_SRC_ADDR_MODE(mode) ((mode) << 5)
32b096c137SEmilio López #define SUN4I_DMA_CFG_SRC_DRQ_TYPE(type) (type)
33b096c137SEmilio López
34b096c137SEmilio López /** Normal DMA register values **/
35b096c137SEmilio López
36b096c137SEmilio López /* Normal DMA source/destination data request type values */
37b096c137SEmilio López #define SUN4I_NDMA_DRQ_TYPE_SDRAM 0x16
38b096c137SEmilio López #define SUN4I_NDMA_DRQ_TYPE_LIMIT (0x1F + 1)
39b096c137SEmilio López
40b096c137SEmilio López /** Normal DMA register layout **/
41b096c137SEmilio López
42b096c137SEmilio López /* Dedicated DMA source/destination address mode values */
43b096c137SEmilio López #define SUN4I_NDMA_ADDR_MODE_LINEAR 0
44b096c137SEmilio López #define SUN4I_NDMA_ADDR_MODE_IO 1
45b096c137SEmilio López
46b096c137SEmilio López /* Normal DMA configuration register layout */
47b096c137SEmilio López #define SUN4I_NDMA_CFG_CONT_MODE BIT(30)
48b096c137SEmilio López #define SUN4I_NDMA_CFG_WAIT_STATE(n) ((n) << 27)
49b096c137SEmilio López #define SUN4I_NDMA_CFG_DST_NON_SECURE BIT(22)
50b096c137SEmilio López #define SUN4I_NDMA_CFG_BYTE_COUNT_MODE_REMAIN BIT(15)
51b096c137SEmilio López #define SUN4I_NDMA_CFG_SRC_NON_SECURE BIT(6)
52b096c137SEmilio López
53b096c137SEmilio López /** Dedicated DMA register values **/
54b096c137SEmilio López
55b096c137SEmilio López /* Dedicated DMA source/destination address mode values */
56b096c137SEmilio López #define SUN4I_DDMA_ADDR_MODE_LINEAR 0
57b096c137SEmilio López #define SUN4I_DDMA_ADDR_MODE_IO 1
58b096c137SEmilio López #define SUN4I_DDMA_ADDR_MODE_HORIZONTAL_PAGE 2
59b096c137SEmilio López #define SUN4I_DDMA_ADDR_MODE_VERTICAL_PAGE 3
60b096c137SEmilio López
61b096c137SEmilio López /* Dedicated DMA source/destination data request type values */
62b096c137SEmilio López #define SUN4I_DDMA_DRQ_TYPE_SDRAM 0x1
63b096c137SEmilio López #define SUN4I_DDMA_DRQ_TYPE_LIMIT (0x1F + 1)
64b096c137SEmilio López
65b096c137SEmilio López /** Dedicated DMA register layout **/
66b096c137SEmilio López
67b096c137SEmilio López /* Dedicated DMA configuration register layout */
68b096c137SEmilio López #define SUN4I_DDMA_CFG_BUSY BIT(30)
69b096c137SEmilio López #define SUN4I_DDMA_CFG_CONT_MODE BIT(29)
70b096c137SEmilio López #define SUN4I_DDMA_CFG_DST_NON_SECURE BIT(28)
71b096c137SEmilio López #define SUN4I_DDMA_CFG_BYTE_COUNT_MODE_REMAIN BIT(15)
72b096c137SEmilio López #define SUN4I_DDMA_CFG_SRC_NON_SECURE BIT(12)
73b096c137SEmilio López
74b096c137SEmilio López /* Dedicated DMA parameter register layout */
75b096c137SEmilio López #define SUN4I_DDMA_PARA_DST_DATA_BLK_SIZE(n) (((n) - 1) << 24)
76b096c137SEmilio López #define SUN4I_DDMA_PARA_DST_WAIT_CYCLES(n) (((n) - 1) << 16)
77b096c137SEmilio López #define SUN4I_DDMA_PARA_SRC_DATA_BLK_SIZE(n) (((n) - 1) << 8)
78b096c137SEmilio López #define SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(n) (((n) - 1) << 0)
79b096c137SEmilio López
80b096c137SEmilio López /** DMA register offsets **/
81b096c137SEmilio López
82b096c137SEmilio López /* General register offsets */
83b096c137SEmilio López #define SUN4I_DMA_IRQ_ENABLE_REG 0x0
84b096c137SEmilio López #define SUN4I_DMA_IRQ_PENDING_STATUS_REG 0x4
85b096c137SEmilio López
86b096c137SEmilio López /* Normal DMA register offsets */
87b096c137SEmilio López #define SUN4I_NDMA_CHANNEL_REG_BASE(n) (0x100 + (n) * 0x20)
88b096c137SEmilio López #define SUN4I_NDMA_CFG_REG 0x0
89b096c137SEmilio López #define SUN4I_NDMA_SRC_ADDR_REG 0x4
90b096c137SEmilio López #define SUN4I_NDMA_DST_ADDR_REG 0x8
91b096c137SEmilio López #define SUN4I_NDMA_BYTE_COUNT_REG 0xC
92b096c137SEmilio López
93b096c137SEmilio López /* Dedicated DMA register offsets */
94b096c137SEmilio López #define SUN4I_DDMA_CHANNEL_REG_BASE(n) (0x300 + (n) * 0x20)
95b096c137SEmilio López #define SUN4I_DDMA_CFG_REG 0x0
96b096c137SEmilio López #define SUN4I_DDMA_SRC_ADDR_REG 0x4
97b096c137SEmilio López #define SUN4I_DDMA_DST_ADDR_REG 0x8
98b096c137SEmilio López #define SUN4I_DDMA_BYTE_COUNT_REG 0xC
99b096c137SEmilio López #define SUN4I_DDMA_PARA_REG 0x18
100b096c137SEmilio López
101b096c137SEmilio López /** DMA Driver **/
102b096c137SEmilio López
103b096c137SEmilio López /*
104b096c137SEmilio López * Normal DMA has 8 channels, and Dedicated DMA has another 8, so
105b096c137SEmilio López * that's 16 channels. As for endpoints, there's 29 and 21
106b096c137SEmilio López * respectively. Given that the Normal DMA endpoints (other than
107b096c137SEmilio López * SDRAM) can be used as tx/rx, we need 78 vchans in total
108b096c137SEmilio López */
109b096c137SEmilio López #define SUN4I_NDMA_NR_MAX_CHANNELS 8
110b096c137SEmilio López #define SUN4I_DDMA_NR_MAX_CHANNELS 8
111b096c137SEmilio López #define SUN4I_DMA_NR_MAX_CHANNELS \
112b096c137SEmilio López (SUN4I_NDMA_NR_MAX_CHANNELS + SUN4I_DDMA_NR_MAX_CHANNELS)
113b096c137SEmilio López #define SUN4I_NDMA_NR_MAX_VCHANS (29 * 2 - 1)
114b096c137SEmilio López #define SUN4I_DDMA_NR_MAX_VCHANS 21
115b096c137SEmilio López #define SUN4I_DMA_NR_MAX_VCHANS \
116b096c137SEmilio López (SUN4I_NDMA_NR_MAX_VCHANS + SUN4I_DDMA_NR_MAX_VCHANS)
117b096c137SEmilio López
118b096c137SEmilio López /* This set of SUN4I_DDMA timing parameters were found experimentally while
119b096c137SEmilio López * working with the SPI driver and seem to make it behave correctly */
120b096c137SEmilio López #define SUN4I_DDMA_MAGIC_SPI_PARAMETERS \
121b096c137SEmilio López (SUN4I_DDMA_PARA_DST_DATA_BLK_SIZE(1) | \
122b096c137SEmilio López SUN4I_DDMA_PARA_SRC_DATA_BLK_SIZE(1) | \
123b096c137SEmilio López SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) | \
124b096c137SEmilio López SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2))
125b096c137SEmilio López
126a94a098aSSamuel Holland /*
127a94a098aSSamuel Holland * Normal DMA supports individual transfers (segments) up to 128k.
128a94a098aSSamuel Holland * Dedicated DMA supports transfers up to 16M. We can only report
129a94a098aSSamuel Holland * one size limit, so we have to use the smaller value.
130a94a098aSSamuel Holland */
131a94a098aSSamuel Holland #define SUN4I_NDMA_MAX_SEG_SIZE SZ_128K
132a94a098aSSamuel Holland #define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M
133a94a098aSSamuel Holland #define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE
134a94a098aSSamuel Holland
135b096c137SEmilio López struct sun4i_dma_pchan {
136b096c137SEmilio López /* Register base of channel */
137b096c137SEmilio López void __iomem *base;
138b096c137SEmilio López /* vchan currently being serviced */
139b096c137SEmilio López struct sun4i_dma_vchan *vchan;
140b096c137SEmilio López /* Is this a dedicated pchan? */
141b096c137SEmilio López int is_dedicated;
142b096c137SEmilio López };
143b096c137SEmilio López
144b096c137SEmilio López struct sun4i_dma_vchan {
145b096c137SEmilio López struct virt_dma_chan vc;
146b096c137SEmilio López struct dma_slave_config cfg;
147b096c137SEmilio López struct sun4i_dma_pchan *pchan;
148b096c137SEmilio López struct sun4i_dma_promise *processing;
149b096c137SEmilio López struct sun4i_dma_contract *contract;
150b096c137SEmilio López u8 endpoint;
151b096c137SEmilio López int is_dedicated;
152b096c137SEmilio López };
153b096c137SEmilio López
154b096c137SEmilio López struct sun4i_dma_promise {
155b096c137SEmilio López u32 cfg;
156b096c137SEmilio López u32 para;
157b096c137SEmilio López dma_addr_t src;
158b096c137SEmilio López dma_addr_t dst;
159b096c137SEmilio López size_t len;
160b096c137SEmilio López struct list_head list;
161b096c137SEmilio López };
162b096c137SEmilio López
163b096c137SEmilio López /* A contract is a set of promises */
164b096c137SEmilio López struct sun4i_dma_contract {
165b096c137SEmilio López struct virt_dma_desc vd;
166b096c137SEmilio López struct list_head demands;
167b096c137SEmilio López struct list_head completed_demands;
168a94a098aSSamuel Holland bool is_cyclic : 1;
169a94a098aSSamuel Holland bool use_half_int : 1;
170b096c137SEmilio López };
171b096c137SEmilio López
172b096c137SEmilio López struct sun4i_dma_dev {
173b096c137SEmilio López DECLARE_BITMAP(pchans_used, SUN4I_DMA_NR_MAX_CHANNELS);
174b096c137SEmilio López struct dma_device slave;
175b096c137SEmilio López struct sun4i_dma_pchan *pchans;
176b096c137SEmilio López struct sun4i_dma_vchan *vchans;
177b096c137SEmilio López void __iomem *base;
178b096c137SEmilio López struct clk *clk;
179b096c137SEmilio López int irq;
180b096c137SEmilio López spinlock_t lock;
181b096c137SEmilio López };
182b096c137SEmilio López
to_sun4i_dma_dev(struct dma_device * dev)183b096c137SEmilio López static struct sun4i_dma_dev *to_sun4i_dma_dev(struct dma_device *dev)
184b096c137SEmilio López {
185b096c137SEmilio López return container_of(dev, struct sun4i_dma_dev, slave);
186b096c137SEmilio López }
187b096c137SEmilio López
to_sun4i_dma_vchan(struct dma_chan * chan)188b096c137SEmilio López static struct sun4i_dma_vchan *to_sun4i_dma_vchan(struct dma_chan *chan)
189b096c137SEmilio López {
190b096c137SEmilio López return container_of(chan, struct sun4i_dma_vchan, vc.chan);
191b096c137SEmilio López }
192b096c137SEmilio López
to_sun4i_dma_contract(struct virt_dma_desc * vd)193b096c137SEmilio López static struct sun4i_dma_contract *to_sun4i_dma_contract(struct virt_dma_desc *vd)
194b096c137SEmilio López {
195b096c137SEmilio López return container_of(vd, struct sun4i_dma_contract, vd);
196b096c137SEmilio López }
197b096c137SEmilio López
chan2dev(struct dma_chan * chan)198b096c137SEmilio López static struct device *chan2dev(struct dma_chan *chan)
199b096c137SEmilio López {
200b096c137SEmilio López return &chan->dev->device;
201b096c137SEmilio López }
202b096c137SEmilio López
convert_burst(u32 maxburst)203b096c137SEmilio López static int convert_burst(u32 maxburst)
204b096c137SEmilio López {
205b096c137SEmilio López if (maxburst > 8)
206b096c137SEmilio López return -EINVAL;
207b096c137SEmilio López
208b096c137SEmilio López /* 1 -> 0, 4 -> 1, 8 -> 2 */
209b096c137SEmilio López return (maxburst >> 2);
210b096c137SEmilio López }
211b096c137SEmilio López
convert_buswidth(enum dma_slave_buswidth addr_width)212b096c137SEmilio López static int convert_buswidth(enum dma_slave_buswidth addr_width)
213b096c137SEmilio López {
214b096c137SEmilio López if (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)
215b096c137SEmilio López return -EINVAL;
216b096c137SEmilio López
217b096c137SEmilio López /* 8 (1 byte) -> 0, 16 (2 bytes) -> 1, 32 (4 bytes) -> 2 */
218b096c137SEmilio López return (addr_width >> 1);
219b096c137SEmilio López }
220b096c137SEmilio López
sun4i_dma_free_chan_resources(struct dma_chan * chan)221b096c137SEmilio López static void sun4i_dma_free_chan_resources(struct dma_chan *chan)
222b096c137SEmilio López {
223b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
224b096c137SEmilio López
225b096c137SEmilio López vchan_free_chan_resources(&vchan->vc);
226b096c137SEmilio López }
227b096c137SEmilio López
find_and_use_pchan(struct sun4i_dma_dev * priv,struct sun4i_dma_vchan * vchan)228b096c137SEmilio López static struct sun4i_dma_pchan *find_and_use_pchan(struct sun4i_dma_dev *priv,
229b096c137SEmilio López struct sun4i_dma_vchan *vchan)
230b096c137SEmilio López {
231b096c137SEmilio López struct sun4i_dma_pchan *pchan = NULL, *pchans = priv->pchans;
232b096c137SEmilio López unsigned long flags;
233b096c137SEmilio López int i, max;
234b096c137SEmilio López
235b096c137SEmilio López /*
236b096c137SEmilio López * pchans 0-SUN4I_NDMA_NR_MAX_CHANNELS are normal, and
237b096c137SEmilio López * SUN4I_NDMA_NR_MAX_CHANNELS+ are dedicated ones
238b096c137SEmilio López */
239b096c137SEmilio López if (vchan->is_dedicated) {
240b096c137SEmilio López i = SUN4I_NDMA_NR_MAX_CHANNELS;
241b096c137SEmilio López max = SUN4I_DMA_NR_MAX_CHANNELS;
242b096c137SEmilio López } else {
243b096c137SEmilio López i = 0;
244b096c137SEmilio López max = SUN4I_NDMA_NR_MAX_CHANNELS;
245b096c137SEmilio López }
246b096c137SEmilio López
247b096c137SEmilio López spin_lock_irqsave(&priv->lock, flags);
24857192245SMarc Gonzalez for_each_clear_bit_from(i, priv->pchans_used, max) {
249b096c137SEmilio López pchan = &pchans[i];
250b096c137SEmilio López pchan->vchan = vchan;
251b096c137SEmilio López set_bit(i, priv->pchans_used);
252b096c137SEmilio López break;
253b096c137SEmilio López }
254b096c137SEmilio López spin_unlock_irqrestore(&priv->lock, flags);
255b096c137SEmilio López
256b096c137SEmilio López return pchan;
257b096c137SEmilio López }
258b096c137SEmilio López
release_pchan(struct sun4i_dma_dev * priv,struct sun4i_dma_pchan * pchan)259b096c137SEmilio López static void release_pchan(struct sun4i_dma_dev *priv,
260b096c137SEmilio López struct sun4i_dma_pchan *pchan)
261b096c137SEmilio López {
262b096c137SEmilio López unsigned long flags;
263b096c137SEmilio López int nr = pchan - priv->pchans;
264b096c137SEmilio López
265b096c137SEmilio López spin_lock_irqsave(&priv->lock, flags);
266b096c137SEmilio López
267b096c137SEmilio López pchan->vchan = NULL;
268b096c137SEmilio López clear_bit(nr, priv->pchans_used);
269b096c137SEmilio López
270b096c137SEmilio López spin_unlock_irqrestore(&priv->lock, flags);
271b096c137SEmilio López }
272b096c137SEmilio López
configure_pchan(struct sun4i_dma_pchan * pchan,struct sun4i_dma_promise * d)273b096c137SEmilio López static void configure_pchan(struct sun4i_dma_pchan *pchan,
274b096c137SEmilio López struct sun4i_dma_promise *d)
275b096c137SEmilio López {
276b096c137SEmilio López /*
277b096c137SEmilio López * Configure addresses and misc parameters depending on type
278b096c137SEmilio López * SUN4I_DDMA has an extra field with timing parameters
279b096c137SEmilio López */
280b096c137SEmilio López if (pchan->is_dedicated) {
281b096c137SEmilio López writel_relaxed(d->src, pchan->base + SUN4I_DDMA_SRC_ADDR_REG);
282b096c137SEmilio López writel_relaxed(d->dst, pchan->base + SUN4I_DDMA_DST_ADDR_REG);
283b096c137SEmilio López writel_relaxed(d->len, pchan->base + SUN4I_DDMA_BYTE_COUNT_REG);
284b096c137SEmilio López writel_relaxed(d->para, pchan->base + SUN4I_DDMA_PARA_REG);
285b096c137SEmilio López writel_relaxed(d->cfg, pchan->base + SUN4I_DDMA_CFG_REG);
286b096c137SEmilio López } else {
287b096c137SEmilio López writel_relaxed(d->src, pchan->base + SUN4I_NDMA_SRC_ADDR_REG);
288b096c137SEmilio López writel_relaxed(d->dst, pchan->base + SUN4I_NDMA_DST_ADDR_REG);
289b096c137SEmilio López writel_relaxed(d->len, pchan->base + SUN4I_NDMA_BYTE_COUNT_REG);
290b096c137SEmilio López writel_relaxed(d->cfg, pchan->base + SUN4I_NDMA_CFG_REG);
291b096c137SEmilio López }
292b096c137SEmilio López }
293b096c137SEmilio López
set_pchan_interrupt(struct sun4i_dma_dev * priv,struct sun4i_dma_pchan * pchan,int half,int end)294b096c137SEmilio López static void set_pchan_interrupt(struct sun4i_dma_dev *priv,
295b096c137SEmilio López struct sun4i_dma_pchan *pchan,
296b096c137SEmilio López int half, int end)
297b096c137SEmilio López {
298b096c137SEmilio López u32 reg;
299b096c137SEmilio López int pchan_number = pchan - priv->pchans;
300b096c137SEmilio López unsigned long flags;
301b096c137SEmilio López
302b096c137SEmilio López spin_lock_irqsave(&priv->lock, flags);
303b096c137SEmilio López
304b096c137SEmilio López reg = readl_relaxed(priv->base + SUN4I_DMA_IRQ_ENABLE_REG);
305b096c137SEmilio López
306b096c137SEmilio López if (half)
307b096c137SEmilio López reg |= BIT(pchan_number * 2);
308b096c137SEmilio López else
309b096c137SEmilio López reg &= ~BIT(pchan_number * 2);
310b096c137SEmilio López
311b096c137SEmilio López if (end)
312b096c137SEmilio López reg |= BIT(pchan_number * 2 + 1);
313b096c137SEmilio López else
314b096c137SEmilio López reg &= ~BIT(pchan_number * 2 + 1);
315b096c137SEmilio López
316b096c137SEmilio López writel_relaxed(reg, priv->base + SUN4I_DMA_IRQ_ENABLE_REG);
317b096c137SEmilio López
318b096c137SEmilio López spin_unlock_irqrestore(&priv->lock, flags);
319b096c137SEmilio López }
320b096c137SEmilio López
321023069baSLee Jones /*
322b096c137SEmilio López * Execute pending operations on a vchan
323b096c137SEmilio López *
324b096c137SEmilio López * When given a vchan, this function will try to acquire a suitable
325b096c137SEmilio López * pchan and, if successful, will configure it to fulfill a promise
326b096c137SEmilio López * from the next pending contract.
327b096c137SEmilio López *
328b096c137SEmilio López * This function must be called with &vchan->vc.lock held.
329b096c137SEmilio López */
__execute_vchan_pending(struct sun4i_dma_dev * priv,struct sun4i_dma_vchan * vchan)330b096c137SEmilio López static int __execute_vchan_pending(struct sun4i_dma_dev *priv,
331b096c137SEmilio López struct sun4i_dma_vchan *vchan)
332b096c137SEmilio López {
333b096c137SEmilio López struct sun4i_dma_promise *promise = NULL;
334b096c137SEmilio López struct sun4i_dma_contract *contract = NULL;
335b096c137SEmilio López struct sun4i_dma_pchan *pchan;
336b096c137SEmilio López struct virt_dma_desc *vd;
337b096c137SEmilio López int ret;
338b096c137SEmilio López
339b096c137SEmilio López lockdep_assert_held(&vchan->vc.lock);
340b096c137SEmilio López
341b096c137SEmilio López /* We need a pchan to do anything, so secure one if available */
342b096c137SEmilio López pchan = find_and_use_pchan(priv, vchan);
343b096c137SEmilio López if (!pchan)
344b096c137SEmilio López return -EBUSY;
345b096c137SEmilio López
346b096c137SEmilio López /*
347b096c137SEmilio López * Channel endpoints must not be repeated, so if this vchan
348b096c137SEmilio López * has already submitted some work, we can't do anything else
349b096c137SEmilio López */
350b096c137SEmilio López if (vchan->processing) {
351b096c137SEmilio López dev_dbg(chan2dev(&vchan->vc.chan),
352b096c137SEmilio López "processing something to this endpoint already\n");
353b096c137SEmilio López ret = -EBUSY;
354b096c137SEmilio López goto release_pchan;
355b096c137SEmilio López }
356b096c137SEmilio López
357b096c137SEmilio López do {
358b096c137SEmilio López /* Figure out which contract we're working with today */
359b096c137SEmilio López vd = vchan_next_desc(&vchan->vc);
360b096c137SEmilio López if (!vd) {
361b096c137SEmilio López dev_dbg(chan2dev(&vchan->vc.chan),
362b096c137SEmilio López "No pending contract found");
363b096c137SEmilio López ret = 0;
364b096c137SEmilio López goto release_pchan;
365b096c137SEmilio López }
366b096c137SEmilio López
367b096c137SEmilio López contract = to_sun4i_dma_contract(vd);
368b096c137SEmilio López if (list_empty(&contract->demands)) {
369b096c137SEmilio López /* The contract has been completed so mark it as such */
370b096c137SEmilio López list_del(&contract->vd.node);
371b096c137SEmilio López vchan_cookie_complete(&contract->vd);
372b096c137SEmilio López dev_dbg(chan2dev(&vchan->vc.chan),
373b096c137SEmilio López "Empty contract found and marked complete");
374b096c137SEmilio López }
375b096c137SEmilio López } while (list_empty(&contract->demands));
376b096c137SEmilio López
377b096c137SEmilio López /* Now find out what we need to do */
378b096c137SEmilio López promise = list_first_entry(&contract->demands,
379b096c137SEmilio López struct sun4i_dma_promise, list);
380b096c137SEmilio López vchan->processing = promise;
381b096c137SEmilio López
382b096c137SEmilio López /* ... and make it reality */
383b096c137SEmilio López if (promise) {
384b096c137SEmilio López vchan->contract = contract;
385b096c137SEmilio López vchan->pchan = pchan;
386a94a098aSSamuel Holland set_pchan_interrupt(priv, pchan, contract->use_half_int, 1);
387b096c137SEmilio López configure_pchan(pchan, promise);
388b096c137SEmilio López }
389b096c137SEmilio López
390b096c137SEmilio López return 0;
391b096c137SEmilio López
392b096c137SEmilio López release_pchan:
393b096c137SEmilio López release_pchan(priv, pchan);
394b096c137SEmilio López return ret;
395b096c137SEmilio López }
396b096c137SEmilio López
sanitize_config(struct dma_slave_config * sconfig,enum dma_transfer_direction direction)397b096c137SEmilio López static int sanitize_config(struct dma_slave_config *sconfig,
398b096c137SEmilio López enum dma_transfer_direction direction)
399b096c137SEmilio López {
400b096c137SEmilio López switch (direction) {
401b096c137SEmilio López case DMA_MEM_TO_DEV:
402b096c137SEmilio López if ((sconfig->dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) ||
403b096c137SEmilio López !sconfig->dst_maxburst)
404b096c137SEmilio López return -EINVAL;
405b096c137SEmilio López
406b096c137SEmilio López if (sconfig->src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
407b096c137SEmilio López sconfig->src_addr_width = sconfig->dst_addr_width;
408b096c137SEmilio López
409b096c137SEmilio López if (!sconfig->src_maxburst)
410b096c137SEmilio López sconfig->src_maxburst = sconfig->dst_maxburst;
411b096c137SEmilio López
412b096c137SEmilio López break;
413b096c137SEmilio López
414b096c137SEmilio López case DMA_DEV_TO_MEM:
415b096c137SEmilio López if ((sconfig->src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) ||
416b096c137SEmilio López !sconfig->src_maxburst)
417b096c137SEmilio López return -EINVAL;
418b096c137SEmilio López
419b096c137SEmilio López if (sconfig->dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
420b096c137SEmilio López sconfig->dst_addr_width = sconfig->src_addr_width;
421b096c137SEmilio López
422b096c137SEmilio López if (!sconfig->dst_maxburst)
423b096c137SEmilio López sconfig->dst_maxburst = sconfig->src_maxburst;
424b096c137SEmilio López
425b096c137SEmilio López break;
426b096c137SEmilio López default:
427b096c137SEmilio López return 0;
428b096c137SEmilio López }
429b096c137SEmilio López
430b096c137SEmilio López return 0;
431b096c137SEmilio López }
432b096c137SEmilio López
433023069baSLee Jones /*
434b096c137SEmilio López * Generate a promise, to be used in a normal DMA contract.
435b096c137SEmilio López *
436b096c137SEmilio López * A NDMA promise contains all the information required to program the
437b096c137SEmilio López * normal part of the DMA Engine and get data copied. A non-executed
438b096c137SEmilio López * promise will live in the demands list on a contract. Once it has been
439b096c137SEmilio López * completed, it will be moved to the completed demands list for later freeing.
440b096c137SEmilio López * All linked promises will be freed when the corresponding contract is freed
441b096c137SEmilio López */
442b096c137SEmilio López static struct sun4i_dma_promise *
generate_ndma_promise(struct dma_chan * chan,dma_addr_t src,dma_addr_t dest,size_t len,struct dma_slave_config * sconfig,enum dma_transfer_direction direction)443b096c137SEmilio López generate_ndma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
444b096c137SEmilio López size_t len, struct dma_slave_config *sconfig,
445b096c137SEmilio López enum dma_transfer_direction direction)
446b096c137SEmilio López {
447b096c137SEmilio López struct sun4i_dma_promise *promise;
448b096c137SEmilio López int ret;
449b096c137SEmilio López
450b096c137SEmilio López ret = sanitize_config(sconfig, direction);
451b096c137SEmilio López if (ret)
452b096c137SEmilio López return NULL;
453b096c137SEmilio López
454b096c137SEmilio López promise = kzalloc(sizeof(*promise), GFP_NOWAIT);
455b096c137SEmilio López if (!promise)
456b096c137SEmilio López return NULL;
457b096c137SEmilio López
458b096c137SEmilio López promise->src = src;
459b096c137SEmilio López promise->dst = dest;
460b096c137SEmilio López promise->len = len;
461b096c137SEmilio López promise->cfg = SUN4I_DMA_CFG_LOADING |
462b096c137SEmilio López SUN4I_NDMA_CFG_BYTE_COUNT_MODE_REMAIN;
463b096c137SEmilio López
464b096c137SEmilio López dev_dbg(chan2dev(chan),
465b096c137SEmilio López "src burst %d, dst burst %d, src buswidth %d, dst buswidth %d",
466b096c137SEmilio López sconfig->src_maxburst, sconfig->dst_maxburst,
467b096c137SEmilio López sconfig->src_addr_width, sconfig->dst_addr_width);
468b096c137SEmilio López
469b096c137SEmilio López /* Source burst */
470b096c137SEmilio López ret = convert_burst(sconfig->src_maxburst);
471287980e4SArnd Bergmann if (ret < 0)
472b096c137SEmilio López goto fail;
473b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
474b096c137SEmilio López
475b096c137SEmilio López /* Destination burst */
476b096c137SEmilio López ret = convert_burst(sconfig->dst_maxburst);
477287980e4SArnd Bergmann if (ret < 0)
478b096c137SEmilio López goto fail;
479b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
480b096c137SEmilio López
481b096c137SEmilio López /* Source bus width */
482b096c137SEmilio López ret = convert_buswidth(sconfig->src_addr_width);
483287980e4SArnd Bergmann if (ret < 0)
484b096c137SEmilio López goto fail;
485b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
486b096c137SEmilio López
487b096c137SEmilio López /* Destination bus width */
488b096c137SEmilio López ret = convert_buswidth(sconfig->dst_addr_width);
489287980e4SArnd Bergmann if (ret < 0)
490b096c137SEmilio López goto fail;
491b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
492b096c137SEmilio López
493b096c137SEmilio López return promise;
494b096c137SEmilio López
495b096c137SEmilio López fail:
496b096c137SEmilio López kfree(promise);
497b096c137SEmilio López return NULL;
498b096c137SEmilio López }
499b096c137SEmilio López
500023069baSLee Jones /*
501b096c137SEmilio López * Generate a promise, to be used in a dedicated DMA contract.
502b096c137SEmilio López *
503b096c137SEmilio López * A DDMA promise contains all the information required to program the
504b096c137SEmilio López * Dedicated part of the DMA Engine and get data copied. A non-executed
505b096c137SEmilio López * promise will live in the demands list on a contract. Once it has been
506b096c137SEmilio López * completed, it will be moved to the completed demands list for later freeing.
507b096c137SEmilio López * All linked promises will be freed when the corresponding contract is freed
508b096c137SEmilio López */
509b096c137SEmilio López static struct sun4i_dma_promise *
generate_ddma_promise(struct dma_chan * chan,dma_addr_t src,dma_addr_t dest,size_t len,struct dma_slave_config * sconfig)510b096c137SEmilio López generate_ddma_promise(struct dma_chan *chan, dma_addr_t src, dma_addr_t dest,
511b096c137SEmilio López size_t len, struct dma_slave_config *sconfig)
512b096c137SEmilio López {
513b096c137SEmilio López struct sun4i_dma_promise *promise;
514b096c137SEmilio López int ret;
515b096c137SEmilio López
516b096c137SEmilio López promise = kzalloc(sizeof(*promise), GFP_NOWAIT);
517b096c137SEmilio López if (!promise)
518b096c137SEmilio López return NULL;
519b096c137SEmilio López
520b096c137SEmilio López promise->src = src;
521b096c137SEmilio López promise->dst = dest;
522b096c137SEmilio López promise->len = len;
523b096c137SEmilio López promise->cfg = SUN4I_DMA_CFG_LOADING |
524b096c137SEmilio López SUN4I_DDMA_CFG_BYTE_COUNT_MODE_REMAIN;
525b096c137SEmilio López
526b096c137SEmilio López /* Source burst */
527b096c137SEmilio López ret = convert_burst(sconfig->src_maxburst);
528287980e4SArnd Bergmann if (ret < 0)
529b096c137SEmilio López goto fail;
530b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_BURST_LENGTH(ret);
531b096c137SEmilio López
532b096c137SEmilio López /* Destination burst */
533b096c137SEmilio López ret = convert_burst(sconfig->dst_maxburst);
534287980e4SArnd Bergmann if (ret < 0)
535b096c137SEmilio López goto fail;
536b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_DST_BURST_LENGTH(ret);
537b096c137SEmilio López
538b096c137SEmilio López /* Source bus width */
539b096c137SEmilio López ret = convert_buswidth(sconfig->src_addr_width);
540287980e4SArnd Bergmann if (ret < 0)
541b096c137SEmilio López goto fail;
542b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_DATA_WIDTH(ret);
543b096c137SEmilio López
544b096c137SEmilio López /* Destination bus width */
545b096c137SEmilio López ret = convert_buswidth(sconfig->dst_addr_width);
546287980e4SArnd Bergmann if (ret < 0)
547b096c137SEmilio López goto fail;
548b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_DST_DATA_WIDTH(ret);
549b096c137SEmilio López
550b096c137SEmilio López return promise;
551b096c137SEmilio López
552b096c137SEmilio López fail:
553b096c137SEmilio López kfree(promise);
554b096c137SEmilio López return NULL;
555b096c137SEmilio López }
556b096c137SEmilio López
557023069baSLee Jones /*
558b096c137SEmilio López * Generate a contract
559b096c137SEmilio López *
560b096c137SEmilio López * Contracts function as DMA descriptors. As our hardware does not support
561b096c137SEmilio López * linked lists, we need to implement SG via software. We use a contract
562b096c137SEmilio López * to hold all the pieces of the request and process them serially one
563b096c137SEmilio López * after another. Each piece is represented as a promise.
564b096c137SEmilio López */
generate_dma_contract(void)565b096c137SEmilio López static struct sun4i_dma_contract *generate_dma_contract(void)
566b096c137SEmilio López {
567b096c137SEmilio López struct sun4i_dma_contract *contract;
568b096c137SEmilio López
569b096c137SEmilio López contract = kzalloc(sizeof(*contract), GFP_NOWAIT);
570b096c137SEmilio López if (!contract)
571b096c137SEmilio López return NULL;
572b096c137SEmilio López
573b096c137SEmilio López INIT_LIST_HEAD(&contract->demands);
574b096c137SEmilio López INIT_LIST_HEAD(&contract->completed_demands);
575b096c137SEmilio López
576b096c137SEmilio López return contract;
577b096c137SEmilio López }
578b096c137SEmilio López
579023069baSLee Jones /*
580b096c137SEmilio López * Get next promise on a cyclic transfer
581b096c137SEmilio López *
582b096c137SEmilio López * Cyclic contracts contain a series of promises which are executed on a
583b096c137SEmilio López * loop. This function returns the next promise from a cyclic contract,
584b096c137SEmilio López * so it can be programmed into the hardware.
585b096c137SEmilio López */
586b096c137SEmilio López static struct sun4i_dma_promise *
get_next_cyclic_promise(struct sun4i_dma_contract * contract)587b096c137SEmilio López get_next_cyclic_promise(struct sun4i_dma_contract *contract)
588b096c137SEmilio López {
589b096c137SEmilio López struct sun4i_dma_promise *promise;
590b096c137SEmilio López
591b096c137SEmilio López promise = list_first_entry_or_null(&contract->demands,
592b096c137SEmilio López struct sun4i_dma_promise, list);
593b096c137SEmilio López if (!promise) {
594b096c137SEmilio López list_splice_init(&contract->completed_demands,
595b096c137SEmilio López &contract->demands);
596b096c137SEmilio López promise = list_first_entry(&contract->demands,
597b096c137SEmilio López struct sun4i_dma_promise, list);
598b096c137SEmilio López }
599b096c137SEmilio López
600b096c137SEmilio López return promise;
601b096c137SEmilio López }
602b096c137SEmilio López
603023069baSLee Jones /*
604b096c137SEmilio López * Free a contract and all its associated promises
605b096c137SEmilio López */
sun4i_dma_free_contract(struct virt_dma_desc * vd)606b096c137SEmilio López static void sun4i_dma_free_contract(struct virt_dma_desc *vd)
607b096c137SEmilio López {
608b096c137SEmilio López struct sun4i_dma_contract *contract = to_sun4i_dma_contract(vd);
60940482e64SEmilio López struct sun4i_dma_promise *promise, *tmp;
610b096c137SEmilio López
611b096c137SEmilio López /* Free all the demands and completed demands */
61240482e64SEmilio López list_for_each_entry_safe(promise, tmp, &contract->demands, list)
613b096c137SEmilio López kfree(promise);
614b096c137SEmilio López
61540482e64SEmilio López list_for_each_entry_safe(promise, tmp, &contract->completed_demands, list)
616b096c137SEmilio López kfree(promise);
617b096c137SEmilio López
618b096c137SEmilio López kfree(contract);
619b096c137SEmilio López }
620b096c137SEmilio López
621b096c137SEmilio López static struct dma_async_tx_descriptor *
sun4i_dma_prep_dma_memcpy(struct dma_chan * chan,dma_addr_t dest,dma_addr_t src,size_t len,unsigned long flags)622b096c137SEmilio López sun4i_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
623b096c137SEmilio López dma_addr_t src, size_t len, unsigned long flags)
624b096c137SEmilio López {
625b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
626b096c137SEmilio López struct dma_slave_config *sconfig = &vchan->cfg;
627b096c137SEmilio López struct sun4i_dma_promise *promise;
628b096c137SEmilio López struct sun4i_dma_contract *contract;
629b096c137SEmilio López
630b096c137SEmilio López contract = generate_dma_contract();
631b096c137SEmilio López if (!contract)
632b096c137SEmilio López return NULL;
633b096c137SEmilio López
634b096c137SEmilio López /*
635b096c137SEmilio López * We can only do the copy to bus aligned addresses, so
636b096c137SEmilio López * choose the best one so we get decent performance. We also
637b096c137SEmilio López * maximize the burst size for this same reason.
638b096c137SEmilio López */
639b096c137SEmilio López sconfig->src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
640b096c137SEmilio López sconfig->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
641b096c137SEmilio López sconfig->src_maxburst = 8;
642b096c137SEmilio López sconfig->dst_maxburst = 8;
643b096c137SEmilio López
644b096c137SEmilio López if (vchan->is_dedicated)
645b096c137SEmilio López promise = generate_ddma_promise(chan, src, dest, len, sconfig);
646b096c137SEmilio López else
647b096c137SEmilio López promise = generate_ndma_promise(chan, src, dest, len, sconfig,
648b096c137SEmilio López DMA_MEM_TO_MEM);
649b096c137SEmilio López
650b096c137SEmilio López if (!promise) {
651b096c137SEmilio López kfree(contract);
652b096c137SEmilio López return NULL;
653b096c137SEmilio López }
654b096c137SEmilio López
655b096c137SEmilio López /* Configure memcpy mode */
656b096c137SEmilio López if (vchan->is_dedicated) {
657b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM) |
658b096c137SEmilio López SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_DDMA_DRQ_TYPE_SDRAM);
659b096c137SEmilio López } else {
660b096c137SEmilio López promise->cfg |= SUN4I_DMA_CFG_SRC_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM) |
661b096c137SEmilio López SUN4I_DMA_CFG_DST_DRQ_TYPE(SUN4I_NDMA_DRQ_TYPE_SDRAM);
662b096c137SEmilio López }
663b096c137SEmilio López
664b096c137SEmilio López /* Fill the contract with our only promise */
665b096c137SEmilio López list_add_tail(&promise->list, &contract->demands);
666b096c137SEmilio López
667b096c137SEmilio López /* And add it to the vchan */
668b096c137SEmilio López return vchan_tx_prep(&vchan->vc, &contract->vd, flags);
669b096c137SEmilio López }
670b096c137SEmilio López
671b096c137SEmilio López static struct dma_async_tx_descriptor *
sun4i_dma_prep_dma_cyclic(struct dma_chan * chan,dma_addr_t buf,size_t len,size_t period_len,enum dma_transfer_direction dir,unsigned long flags)672b096c137SEmilio López sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len,
673b096c137SEmilio López size_t period_len, enum dma_transfer_direction dir,
674b096c137SEmilio López unsigned long flags)
675b096c137SEmilio López {
676b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
677b096c137SEmilio López struct dma_slave_config *sconfig = &vchan->cfg;
678b096c137SEmilio López struct sun4i_dma_promise *promise;
679b096c137SEmilio López struct sun4i_dma_contract *contract;
680b096c137SEmilio López dma_addr_t src, dest;
681b096c137SEmilio López u32 endpoints;
682b096c137SEmilio López int nr_periods, offset, plength, i;
683ffc079a4SStefan Mavrodiev u8 ram_type, io_mode, linear_mode;
684b096c137SEmilio López
685b096c137SEmilio López if (!is_slave_direction(dir)) {
686b096c137SEmilio López dev_err(chan2dev(chan), "Invalid DMA direction\n");
687b096c137SEmilio López return NULL;
688b096c137SEmilio López }
689b096c137SEmilio López
690b096c137SEmilio López contract = generate_dma_contract();
691b096c137SEmilio López if (!contract)
692b096c137SEmilio López return NULL;
693b096c137SEmilio López
694b096c137SEmilio López contract->is_cyclic = 1;
695b096c137SEmilio López
696ffc079a4SStefan Mavrodiev if (vchan->is_dedicated) {
697ffc079a4SStefan Mavrodiev io_mode = SUN4I_DDMA_ADDR_MODE_IO;
698ffc079a4SStefan Mavrodiev linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
699ffc079a4SStefan Mavrodiev ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
700ffc079a4SStefan Mavrodiev } else {
701ffc079a4SStefan Mavrodiev io_mode = SUN4I_NDMA_ADDR_MODE_IO;
702ffc079a4SStefan Mavrodiev linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
703ffc079a4SStefan Mavrodiev ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
704ffc079a4SStefan Mavrodiev }
705ffc079a4SStefan Mavrodiev
706b096c137SEmilio López if (dir == DMA_MEM_TO_DEV) {
707b096c137SEmilio López src = buf;
708b096c137SEmilio López dest = sconfig->dst_addr;
709ffc079a4SStefan Mavrodiev endpoints = SUN4I_DMA_CFG_DST_DRQ_TYPE(vchan->endpoint) |
710ffc079a4SStefan Mavrodiev SUN4I_DMA_CFG_DST_ADDR_MODE(io_mode) |
7118faa7733SVinod Koul SUN4I_DMA_CFG_SRC_DRQ_TYPE(ram_type) |
7126ebb827fSYueHaibing SUN4I_DMA_CFG_SRC_ADDR_MODE(linear_mode);
713b096c137SEmilio López } else {
714b096c137SEmilio López src = sconfig->src_addr;
715b096c137SEmilio López dest = buf;
716ffc079a4SStefan Mavrodiev endpoints = SUN4I_DMA_CFG_DST_DRQ_TYPE(ram_type) |
7176ebb827fSYueHaibing SUN4I_DMA_CFG_DST_ADDR_MODE(linear_mode) |
718ffc079a4SStefan Mavrodiev SUN4I_DMA_CFG_SRC_DRQ_TYPE(vchan->endpoint) |
719ffc079a4SStefan Mavrodiev SUN4I_DMA_CFG_SRC_ADDR_MODE(io_mode);
720b096c137SEmilio López }
721b096c137SEmilio López
722b096c137SEmilio López /*
723b096c137SEmilio López * We will be using half done interrupts to make two periods
724b096c137SEmilio López * out of a promise, so we need to program the DMA engine less
725b096c137SEmilio López * often
726b096c137SEmilio López */
727b096c137SEmilio López
728b096c137SEmilio López /*
729b096c137SEmilio López * The engine can interrupt on half-transfer, so we can use
730b096c137SEmilio López * this feature to program the engine half as often as if we
731b096c137SEmilio López * didn't use it (keep in mind the hardware doesn't support
732b096c137SEmilio López * linked lists).
733b096c137SEmilio López *
734b096c137SEmilio López * Say you have a set of periods (| marks the start/end, I for
735b096c137SEmilio López * interrupt, P for programming the engine to do a new
736b096c137SEmilio López * transfer), the easy but slow way would be to do
737b096c137SEmilio López *
738b096c137SEmilio López * |---|---|---|---| (periods / promises)
739b096c137SEmilio López * P I,P I,P I,P I
740b096c137SEmilio López *
741b096c137SEmilio López * Using half transfer interrupts you can do
742b096c137SEmilio López *
743b096c137SEmilio López * |-------|-------| (promises as configured on hw)
744b096c137SEmilio López * |---|---|---|---| (periods)
745b096c137SEmilio López * P I I,P I I
746b096c137SEmilio López *
747b096c137SEmilio López * Which requires half the engine programming for the same
748b096c137SEmilio López * functionality.
749a94a098aSSamuel Holland *
750a94a098aSSamuel Holland * This only works if two periods fit in a single promise. That will
751a94a098aSSamuel Holland * always be the case for dedicated DMA, where the hardware has a much
752a94a098aSSamuel Holland * larger maximum transfer size than advertised to clients.
753b096c137SEmilio López */
754a94a098aSSamuel Holland if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) {
755a94a098aSSamuel Holland period_len *= 2;
756a94a098aSSamuel Holland contract->use_half_int = 1;
757a94a098aSSamuel Holland }
758a94a098aSSamuel Holland
759a94a098aSSamuel Holland nr_periods = DIV_ROUND_UP(len, period_len);
760b096c137SEmilio López for (i = 0; i < nr_periods; i++) {
761b096c137SEmilio López /* Calculate the offset in the buffer and the length needed */
762a94a098aSSamuel Holland offset = i * period_len;
763a94a098aSSamuel Holland plength = min((len - offset), period_len);
764b096c137SEmilio López if (dir == DMA_MEM_TO_DEV)
765b096c137SEmilio López src = buf + offset;
766b096c137SEmilio López else
767b096c137SEmilio López dest = buf + offset;
768b096c137SEmilio López
769b096c137SEmilio López /* Make the promise */
770ffc079a4SStefan Mavrodiev if (vchan->is_dedicated)
771ffc079a4SStefan Mavrodiev promise = generate_ddma_promise(chan, src, dest,
772ffc079a4SStefan Mavrodiev plength, sconfig);
773ffc079a4SStefan Mavrodiev else
774b096c137SEmilio López promise = generate_ndma_promise(chan, src, dest,
775b096c137SEmilio López plength, sconfig, dir);
776ffc079a4SStefan Mavrodiev
777b096c137SEmilio López if (!promise) {
778b096c137SEmilio López /* TODO: should we free everything? */
779b096c137SEmilio López return NULL;
780b096c137SEmilio López }
781b096c137SEmilio López promise->cfg |= endpoints;
782b096c137SEmilio López
783b096c137SEmilio López /* Then add it to the contract */
784b096c137SEmilio López list_add_tail(&promise->list, &contract->demands);
785b096c137SEmilio López }
786b096c137SEmilio López
787b096c137SEmilio López /* And add it to the vchan */
788b096c137SEmilio López return vchan_tx_prep(&vchan->vc, &contract->vd, flags);
789b096c137SEmilio López }
790b096c137SEmilio López
791b096c137SEmilio López static struct dma_async_tx_descriptor *
sun4i_dma_prep_slave_sg(struct dma_chan * chan,struct scatterlist * sgl,unsigned int sg_len,enum dma_transfer_direction dir,unsigned long flags,void * context)792b096c137SEmilio López sun4i_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
793b096c137SEmilio López unsigned int sg_len, enum dma_transfer_direction dir,
794b096c137SEmilio López unsigned long flags, void *context)
795b096c137SEmilio López {
796b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
797b096c137SEmilio López struct dma_slave_config *sconfig = &vchan->cfg;
798b096c137SEmilio López struct sun4i_dma_promise *promise;
799b096c137SEmilio López struct sun4i_dma_contract *contract;
800b096c137SEmilio López u8 ram_type, io_mode, linear_mode;
801b096c137SEmilio López struct scatterlist *sg;
802b096c137SEmilio López dma_addr_t srcaddr, dstaddr;
803b096c137SEmilio López u32 endpoints, para;
804b096c137SEmilio López int i;
805b096c137SEmilio López
806b096c137SEmilio López if (!sgl)
807b096c137SEmilio López return NULL;
808b096c137SEmilio López
809b096c137SEmilio López if (!is_slave_direction(dir)) {
810b096c137SEmilio López dev_err(chan2dev(chan), "Invalid DMA direction\n");
811b096c137SEmilio López return NULL;
812b096c137SEmilio López }
813b096c137SEmilio López
814b096c137SEmilio López contract = generate_dma_contract();
815b096c137SEmilio López if (!contract)
816b096c137SEmilio López return NULL;
817b096c137SEmilio López
818b096c137SEmilio López if (vchan->is_dedicated) {
819b096c137SEmilio López io_mode = SUN4I_DDMA_ADDR_MODE_IO;
820b096c137SEmilio López linear_mode = SUN4I_DDMA_ADDR_MODE_LINEAR;
821b096c137SEmilio López ram_type = SUN4I_DDMA_DRQ_TYPE_SDRAM;
822b096c137SEmilio López } else {
823b096c137SEmilio López io_mode = SUN4I_NDMA_ADDR_MODE_IO;
824b096c137SEmilio López linear_mode = SUN4I_NDMA_ADDR_MODE_LINEAR;
825b096c137SEmilio López ram_type = SUN4I_NDMA_DRQ_TYPE_SDRAM;
826b096c137SEmilio López }
827b096c137SEmilio López
828b096c137SEmilio López if (dir == DMA_MEM_TO_DEV)
829b096c137SEmilio López endpoints = SUN4I_DMA_CFG_DST_DRQ_TYPE(vchan->endpoint) |
830b096c137SEmilio López SUN4I_DMA_CFG_DST_ADDR_MODE(io_mode) |
831b096c137SEmilio López SUN4I_DMA_CFG_SRC_DRQ_TYPE(ram_type) |
832b096c137SEmilio López SUN4I_DMA_CFG_SRC_ADDR_MODE(linear_mode);
833b096c137SEmilio López else
834b096c137SEmilio López endpoints = SUN4I_DMA_CFG_DST_DRQ_TYPE(ram_type) |
835b096c137SEmilio López SUN4I_DMA_CFG_DST_ADDR_MODE(linear_mode) |
836b096c137SEmilio López SUN4I_DMA_CFG_SRC_DRQ_TYPE(vchan->endpoint) |
837b096c137SEmilio López SUN4I_DMA_CFG_SRC_ADDR_MODE(io_mode);
838b096c137SEmilio López
839b096c137SEmilio López for_each_sg(sgl, sg, sg_len, i) {
840b096c137SEmilio López /* Figure out addresses */
841b096c137SEmilio López if (dir == DMA_MEM_TO_DEV) {
842b096c137SEmilio López srcaddr = sg_dma_address(sg);
843b096c137SEmilio López dstaddr = sconfig->dst_addr;
844b096c137SEmilio López } else {
845b096c137SEmilio López srcaddr = sconfig->src_addr;
846b096c137SEmilio López dstaddr = sg_dma_address(sg);
847b096c137SEmilio López }
848b096c137SEmilio López
849b096c137SEmilio López /*
850b096c137SEmilio López * These are the magic DMA engine timings that keep SPI going.
851b096c137SEmilio López * I haven't seen any interface on DMAEngine to configure
852b096c137SEmilio López * timings, and so far they seem to work for everything we
853b096c137SEmilio López * support, so I've kept them here. I don't know if other
854b096c137SEmilio López * devices need different timings because, as usual, we only
855b096c137SEmilio López * have the "para" bitfield meanings, but no comment on what
856b096c137SEmilio López * the values should be when doing a certain operation :|
857b096c137SEmilio López */
858b096c137SEmilio López para = SUN4I_DDMA_MAGIC_SPI_PARAMETERS;
859b096c137SEmilio López
860b096c137SEmilio López /* And make a suitable promise */
861b096c137SEmilio López if (vchan->is_dedicated)
862b096c137SEmilio López promise = generate_ddma_promise(chan, srcaddr, dstaddr,
863b096c137SEmilio López sg_dma_len(sg),
864b096c137SEmilio López sconfig);
865b096c137SEmilio López else
866b096c137SEmilio López promise = generate_ndma_promise(chan, srcaddr, dstaddr,
867b096c137SEmilio López sg_dma_len(sg),
868b096c137SEmilio López sconfig, dir);
869b096c137SEmilio López
870b096c137SEmilio López if (!promise)
871b096c137SEmilio López return NULL; /* TODO: should we free everything? */
872b096c137SEmilio López
873b096c137SEmilio López promise->cfg |= endpoints;
874b096c137SEmilio López promise->para = para;
875b096c137SEmilio López
876b096c137SEmilio López /* Then add it to the contract */
877b096c137SEmilio López list_add_tail(&promise->list, &contract->demands);
878b096c137SEmilio López }
879b096c137SEmilio López
880b096c137SEmilio López /*
881b096c137SEmilio López * Once we've got all the promises ready, add the contract
882b096c137SEmilio López * to the pending list on the vchan
883b096c137SEmilio López */
884b096c137SEmilio López return vchan_tx_prep(&vchan->vc, &contract->vd, flags);
885b096c137SEmilio López }
886b096c137SEmilio López
sun4i_dma_terminate_all(struct dma_chan * chan)887b096c137SEmilio López static int sun4i_dma_terminate_all(struct dma_chan *chan)
888b096c137SEmilio López {
889b096c137SEmilio López struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
890b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
891b096c137SEmilio López struct sun4i_dma_pchan *pchan = vchan->pchan;
892b096c137SEmilio López LIST_HEAD(head);
893b096c137SEmilio López unsigned long flags;
894b096c137SEmilio López
895b096c137SEmilio López spin_lock_irqsave(&vchan->vc.lock, flags);
896b096c137SEmilio López vchan_get_all_descriptors(&vchan->vc, &head);
897b096c137SEmilio López spin_unlock_irqrestore(&vchan->vc.lock, flags);
898b096c137SEmilio López
899b096c137SEmilio López /*
900b096c137SEmilio López * Clearing the configuration register will halt the pchan. Interrupts
901b096c137SEmilio López * may still trigger, so don't forget to disable them.
902b096c137SEmilio López */
903b096c137SEmilio López if (pchan) {
904b096c137SEmilio López if (pchan->is_dedicated)
905b096c137SEmilio López writel(0, pchan->base + SUN4I_DDMA_CFG_REG);
906b096c137SEmilio López else
907b096c137SEmilio López writel(0, pchan->base + SUN4I_NDMA_CFG_REG);
908b096c137SEmilio López set_pchan_interrupt(priv, pchan, 0, 0);
909b096c137SEmilio López release_pchan(priv, pchan);
910b096c137SEmilio López }
911b096c137SEmilio López
912b096c137SEmilio López spin_lock_irqsave(&vchan->vc.lock, flags);
913b096c137SEmilio López /* Clear these so the vchan is usable again */
914b096c137SEmilio López vchan->processing = NULL;
915b096c137SEmilio López vchan->pchan = NULL;
916b096c137SEmilio López spin_unlock_irqrestore(&vchan->vc.lock, flags);
917b096c137SEmilio López
91851fe9cd2SSascha Hauer vchan_dma_desc_free_list(&vchan->vc, &head);
91951fe9cd2SSascha Hauer
920b096c137SEmilio López return 0;
921b096c137SEmilio López }
922b096c137SEmilio López
sun4i_dma_config(struct dma_chan * chan,struct dma_slave_config * config)923b096c137SEmilio López static int sun4i_dma_config(struct dma_chan *chan,
924b096c137SEmilio López struct dma_slave_config *config)
925b096c137SEmilio López {
926b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
927b096c137SEmilio López
928b096c137SEmilio López memcpy(&vchan->cfg, config, sizeof(*config));
929b096c137SEmilio López
930b096c137SEmilio López return 0;
931b096c137SEmilio López }
932b096c137SEmilio López
sun4i_dma_of_xlate(struct of_phandle_args * dma_spec,struct of_dma * ofdma)933b096c137SEmilio López static struct dma_chan *sun4i_dma_of_xlate(struct of_phandle_args *dma_spec,
934b096c137SEmilio López struct of_dma *ofdma)
935b096c137SEmilio López {
936b096c137SEmilio López struct sun4i_dma_dev *priv = ofdma->of_dma_data;
937b096c137SEmilio López struct sun4i_dma_vchan *vchan;
938b096c137SEmilio López struct dma_chan *chan;
939b096c137SEmilio López u8 is_dedicated = dma_spec->args[0];
940b096c137SEmilio López u8 endpoint = dma_spec->args[1];
941b096c137SEmilio López
942b096c137SEmilio López /* Check if type is Normal or Dedicated */
943b096c137SEmilio López if (is_dedicated != 0 && is_dedicated != 1)
944b096c137SEmilio López return NULL;
945b096c137SEmilio López
946b096c137SEmilio López /* Make sure the endpoint looks sane */
947b096c137SEmilio López if ((is_dedicated && endpoint >= SUN4I_DDMA_DRQ_TYPE_LIMIT) ||
948b096c137SEmilio López (!is_dedicated && endpoint >= SUN4I_NDMA_DRQ_TYPE_LIMIT))
949b096c137SEmilio López return NULL;
950b096c137SEmilio López
951b096c137SEmilio López chan = dma_get_any_slave_channel(&priv->slave);
952b096c137SEmilio López if (!chan)
953b096c137SEmilio López return NULL;
954b096c137SEmilio López
955b096c137SEmilio López /* Assign the endpoint to the vchan */
956b096c137SEmilio López vchan = to_sun4i_dma_vchan(chan);
957b096c137SEmilio López vchan->is_dedicated = is_dedicated;
958b096c137SEmilio López vchan->endpoint = endpoint;
959b096c137SEmilio López
960b096c137SEmilio López return chan;
961b096c137SEmilio López }
962b096c137SEmilio López
sun4i_dma_tx_status(struct dma_chan * chan,dma_cookie_t cookie,struct dma_tx_state * state)963b096c137SEmilio López static enum dma_status sun4i_dma_tx_status(struct dma_chan *chan,
964b096c137SEmilio López dma_cookie_t cookie,
965b096c137SEmilio López struct dma_tx_state *state)
966b096c137SEmilio López {
967b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
968b096c137SEmilio López struct sun4i_dma_pchan *pchan = vchan->pchan;
969b096c137SEmilio López struct sun4i_dma_contract *contract;
970b096c137SEmilio López struct sun4i_dma_promise *promise;
971b096c137SEmilio López struct virt_dma_desc *vd;
972b096c137SEmilio López unsigned long flags;
973b096c137SEmilio López enum dma_status ret;
974b096c137SEmilio López size_t bytes = 0;
975b096c137SEmilio López
976b096c137SEmilio López ret = dma_cookie_status(chan, cookie, state);
977b096c137SEmilio López if (!state || (ret == DMA_COMPLETE))
978b096c137SEmilio López return ret;
979b096c137SEmilio López
980b096c137SEmilio López spin_lock_irqsave(&vchan->vc.lock, flags);
981b096c137SEmilio López vd = vchan_find_desc(&vchan->vc, cookie);
982b096c137SEmilio López if (!vd)
983b096c137SEmilio López goto exit;
984b096c137SEmilio López contract = to_sun4i_dma_contract(vd);
985b096c137SEmilio López
986b096c137SEmilio López list_for_each_entry(promise, &contract->demands, list)
987b096c137SEmilio López bytes += promise->len;
988b096c137SEmilio López
989b096c137SEmilio López /*
990b096c137SEmilio López * The hardware is configured to return the remaining byte
991b096c137SEmilio López * quantity. If possible, replace the first listed element's
992b096c137SEmilio López * full size with the actual remaining amount
993b096c137SEmilio López */
994b096c137SEmilio López promise = list_first_entry_or_null(&contract->demands,
995b096c137SEmilio López struct sun4i_dma_promise, list);
996b096c137SEmilio López if (promise && pchan) {
997b096c137SEmilio López bytes -= promise->len;
998b096c137SEmilio López if (pchan->is_dedicated)
999b096c137SEmilio López bytes += readl(pchan->base + SUN4I_DDMA_BYTE_COUNT_REG);
1000b096c137SEmilio López else
1001b096c137SEmilio López bytes += readl(pchan->base + SUN4I_NDMA_BYTE_COUNT_REG);
1002b096c137SEmilio López }
1003b096c137SEmilio López
1004b096c137SEmilio López exit:
1005b096c137SEmilio López
1006b096c137SEmilio López dma_set_residue(state, bytes);
1007b096c137SEmilio López spin_unlock_irqrestore(&vchan->vc.lock, flags);
1008b096c137SEmilio López
1009b096c137SEmilio López return ret;
1010b096c137SEmilio López }
1011b096c137SEmilio López
sun4i_dma_issue_pending(struct dma_chan * chan)1012b096c137SEmilio López static void sun4i_dma_issue_pending(struct dma_chan *chan)
1013b096c137SEmilio López {
1014b096c137SEmilio López struct sun4i_dma_dev *priv = to_sun4i_dma_dev(chan->device);
1015b096c137SEmilio López struct sun4i_dma_vchan *vchan = to_sun4i_dma_vchan(chan);
1016b096c137SEmilio López unsigned long flags;
1017b096c137SEmilio López
1018b096c137SEmilio López spin_lock_irqsave(&vchan->vc.lock, flags);
1019b096c137SEmilio López
1020b096c137SEmilio López /*
1021b096c137SEmilio López * If there are pending transactions for this vchan, push one of
1022b096c137SEmilio López * them into the engine to get the ball rolling.
1023b096c137SEmilio López */
1024b096c137SEmilio López if (vchan_issue_pending(&vchan->vc))
1025b096c137SEmilio López __execute_vchan_pending(priv, vchan);
1026b096c137SEmilio López
1027b096c137SEmilio López spin_unlock_irqrestore(&vchan->vc.lock, flags);
1028b096c137SEmilio López }
1029b096c137SEmilio López
sun4i_dma_interrupt(int irq,void * dev_id)1030b096c137SEmilio López static irqreturn_t sun4i_dma_interrupt(int irq, void *dev_id)
1031b096c137SEmilio López {
1032b096c137SEmilio López struct sun4i_dma_dev *priv = dev_id;
1033b096c137SEmilio López struct sun4i_dma_pchan *pchans = priv->pchans, *pchan;
1034b096c137SEmilio López struct sun4i_dma_vchan *vchan;
1035b096c137SEmilio López struct sun4i_dma_contract *contract;
1036b096c137SEmilio López struct sun4i_dma_promise *promise;
1037b096c137SEmilio López unsigned long pendirq, irqs, disableirqs;
1038b096c137SEmilio López int bit, i, free_room, allow_mitigation = 1;
1039b096c137SEmilio López
1040b096c137SEmilio López pendirq = readl_relaxed(priv->base + SUN4I_DMA_IRQ_PENDING_STATUS_REG);
1041b096c137SEmilio López
1042b096c137SEmilio López handle_pending:
1043b096c137SEmilio López
1044b096c137SEmilio López disableirqs = 0;
1045b096c137SEmilio López free_room = 0;
1046b096c137SEmilio López
1047b096c137SEmilio López for_each_set_bit(bit, &pendirq, 32) {
1048b096c137SEmilio López pchan = &pchans[bit >> 1];
1049b096c137SEmilio López vchan = pchan->vchan;
1050b096c137SEmilio López if (!vchan) /* a terminated channel may still interrupt */
1051b096c137SEmilio López continue;
1052b096c137SEmilio López contract = vchan->contract;
1053b096c137SEmilio López
1054b096c137SEmilio López /*
1055b096c137SEmilio López * Disable the IRQ and free the pchan if it's an end
1056b096c137SEmilio López * interrupt (odd bit)
1057b096c137SEmilio López */
1058b096c137SEmilio López if (bit & 1) {
1059b096c137SEmilio López spin_lock(&vchan->vc.lock);
1060b096c137SEmilio López
1061b096c137SEmilio López /*
1062b096c137SEmilio López * Move the promise into the completed list now that
1063b096c137SEmilio López * we're done with it
1064b096c137SEmilio López */
10652e5c09d1SZou Wei list_move_tail(&vchan->processing->list,
1066b096c137SEmilio López &contract->completed_demands);
1067b096c137SEmilio López
1068b096c137SEmilio López /*
1069b096c137SEmilio López * Cyclic DMA transfers are special:
1070b096c137SEmilio López * - There's always something we can dispatch
1071b096c137SEmilio López * - We need to run the callback
1072b096c137SEmilio López * - Latency is very important, as this is used by audio
1073b096c137SEmilio López * We therefore just cycle through the list and dispatch
1074b096c137SEmilio López * whatever we have here, reusing the pchan. There's
1075b096c137SEmilio López * no need to run the thread after this.
1076b096c137SEmilio López *
1077b096c137SEmilio López * For non-cyclic transfers we need to look around,
1078b096c137SEmilio López * so we can program some more work, or notify the
1079b096c137SEmilio López * client that their transfers have been completed.
1080b096c137SEmilio López */
1081b096c137SEmilio López if (contract->is_cyclic) {
1082b096c137SEmilio López promise = get_next_cyclic_promise(contract);
1083b096c137SEmilio López vchan->processing = promise;
1084b096c137SEmilio López configure_pchan(pchan, promise);
1085b096c137SEmilio López vchan_cyclic_callback(&contract->vd);
1086b096c137SEmilio López } else {
1087b096c137SEmilio López vchan->processing = NULL;
1088b096c137SEmilio López vchan->pchan = NULL;
1089b096c137SEmilio López
1090b096c137SEmilio López free_room = 1;
1091b096c137SEmilio López disableirqs |= BIT(bit);
1092b096c137SEmilio López release_pchan(priv, pchan);
1093b096c137SEmilio López }
1094b096c137SEmilio López
1095b096c137SEmilio López spin_unlock(&vchan->vc.lock);
1096b096c137SEmilio López } else {
1097b096c137SEmilio López /* Half done interrupt */
1098b096c137SEmilio López if (contract->is_cyclic)
1099b096c137SEmilio López vchan_cyclic_callback(&contract->vd);
1100b096c137SEmilio López else
1101b096c137SEmilio López disableirqs |= BIT(bit);
1102b096c137SEmilio López }
1103b096c137SEmilio López }
1104b096c137SEmilio López
1105b096c137SEmilio López /* Disable the IRQs for events we handled */
1106b096c137SEmilio López spin_lock(&priv->lock);
1107b096c137SEmilio López irqs = readl_relaxed(priv->base + SUN4I_DMA_IRQ_ENABLE_REG);
1108b096c137SEmilio López writel_relaxed(irqs & ~disableirqs,
1109b096c137SEmilio López priv->base + SUN4I_DMA_IRQ_ENABLE_REG);
1110b096c137SEmilio López spin_unlock(&priv->lock);
1111b096c137SEmilio López
1112b096c137SEmilio López /* Writing 1 to the pending field will clear the pending interrupt */
1113b096c137SEmilio López writel_relaxed(pendirq, priv->base + SUN4I_DMA_IRQ_PENDING_STATUS_REG);
1114b096c137SEmilio López
1115b096c137SEmilio López /*
1116b096c137SEmilio López * If a pchan was freed, we may be able to schedule something else,
1117b096c137SEmilio López * so have a look around
1118b096c137SEmilio López */
1119b096c137SEmilio López if (free_room) {
1120b096c137SEmilio López for (i = 0; i < SUN4I_DMA_NR_MAX_VCHANS; i++) {
1121b096c137SEmilio López vchan = &priv->vchans[i];
1122b096c137SEmilio López spin_lock(&vchan->vc.lock);
1123b096c137SEmilio López __execute_vchan_pending(priv, vchan);
1124b096c137SEmilio López spin_unlock(&vchan->vc.lock);
1125b096c137SEmilio López }
1126b096c137SEmilio López }
1127b096c137SEmilio López
1128b096c137SEmilio López /*
1129b096c137SEmilio López * Handle newer interrupts if some showed up, but only do it once
1130b096c137SEmilio López * to avoid a too long a loop
1131b096c137SEmilio López */
1132b096c137SEmilio López if (allow_mitigation) {
1133b096c137SEmilio López pendirq = readl_relaxed(priv->base +
1134b096c137SEmilio López SUN4I_DMA_IRQ_PENDING_STATUS_REG);
1135b096c137SEmilio López if (pendirq) {
1136b096c137SEmilio López allow_mitigation = 0;
1137b096c137SEmilio López goto handle_pending;
1138b096c137SEmilio López }
1139b096c137SEmilio López }
1140b096c137SEmilio López
1141b096c137SEmilio López return IRQ_HANDLED;
1142b096c137SEmilio López }
1143b096c137SEmilio López
sun4i_dma_probe(struct platform_device * pdev)1144b096c137SEmilio López static int sun4i_dma_probe(struct platform_device *pdev)
1145b096c137SEmilio López {
1146b096c137SEmilio López struct sun4i_dma_dev *priv;
1147b096c137SEmilio López int i, j, ret;
1148b096c137SEmilio López
1149b096c137SEmilio López priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
1150b096c137SEmilio López if (!priv)
1151b096c137SEmilio López return -ENOMEM;
1152b096c137SEmilio López
1153*4b23603aSTudor Ambarus priv->base = devm_platform_ioremap_resource(pdev, 0);
1154b096c137SEmilio López if (IS_ERR(priv->base))
1155b096c137SEmilio López return PTR_ERR(priv->base);
1156b096c137SEmilio López
1157b096c137SEmilio López priv->irq = platform_get_irq(pdev, 0);
1158e17be6e1SStephen Boyd if (priv->irq < 0)
1159b096c137SEmilio López return priv->irq;
1160b096c137SEmilio López
1161b096c137SEmilio López priv->clk = devm_clk_get(&pdev->dev, NULL);
1162b096c137SEmilio López if (IS_ERR(priv->clk)) {
1163b096c137SEmilio López dev_err(&pdev->dev, "No clock specified\n");
1164b096c137SEmilio López return PTR_ERR(priv->clk);
1165b096c137SEmilio López }
1166b096c137SEmilio López
1167b096c137SEmilio López platform_set_drvdata(pdev, priv);
1168b096c137SEmilio López spin_lock_init(&priv->lock);
1169b096c137SEmilio López
1170a94a098aSSamuel Holland dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE);
1171a94a098aSSamuel Holland
1172b096c137SEmilio López dma_cap_zero(priv->slave.cap_mask);
1173b096c137SEmilio López dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask);
1174b096c137SEmilio López dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask);
1175b096c137SEmilio López dma_cap_set(DMA_CYCLIC, priv->slave.cap_mask);
1176b096c137SEmilio López dma_cap_set(DMA_SLAVE, priv->slave.cap_mask);
1177b096c137SEmilio López
1178b096c137SEmilio López INIT_LIST_HEAD(&priv->slave.channels);
1179b096c137SEmilio López priv->slave.device_free_chan_resources = sun4i_dma_free_chan_resources;
1180b096c137SEmilio López priv->slave.device_tx_status = sun4i_dma_tx_status;
1181b096c137SEmilio López priv->slave.device_issue_pending = sun4i_dma_issue_pending;
1182b096c137SEmilio López priv->slave.device_prep_slave_sg = sun4i_dma_prep_slave_sg;
1183b096c137SEmilio López priv->slave.device_prep_dma_memcpy = sun4i_dma_prep_dma_memcpy;
1184b096c137SEmilio López priv->slave.device_prep_dma_cyclic = sun4i_dma_prep_dma_cyclic;
1185b096c137SEmilio López priv->slave.device_config = sun4i_dma_config;
1186b096c137SEmilio López priv->slave.device_terminate_all = sun4i_dma_terminate_all;
1187b096c137SEmilio López priv->slave.copy_align = 2;
1188b096c137SEmilio López priv->slave.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
1189b096c137SEmilio López BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
1190b096c137SEmilio López BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
1191b096c137SEmilio López priv->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
1192b096c137SEmilio López BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
1193b096c137SEmilio López BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
1194b096c137SEmilio López priv->slave.directions = BIT(DMA_DEV_TO_MEM) |
1195b096c137SEmilio López BIT(DMA_MEM_TO_DEV);
1196b096c137SEmilio López priv->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
1197b096c137SEmilio López
1198b096c137SEmilio López priv->slave.dev = &pdev->dev;
1199b096c137SEmilio López
1200b096c137SEmilio López priv->pchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_CHANNELS,
1201b096c137SEmilio López sizeof(struct sun4i_dma_pchan), GFP_KERNEL);
1202b096c137SEmilio López priv->vchans = devm_kcalloc(&pdev->dev, SUN4I_DMA_NR_MAX_VCHANS,
1203b096c137SEmilio López sizeof(struct sun4i_dma_vchan), GFP_KERNEL);
1204b096c137SEmilio López if (!priv->vchans || !priv->pchans)
1205b096c137SEmilio López return -ENOMEM;
1206b096c137SEmilio López
1207b096c137SEmilio López /*
1208b096c137SEmilio López * [0..SUN4I_NDMA_NR_MAX_CHANNELS) are normal pchans, and
1209b096c137SEmilio López * [SUN4I_NDMA_NR_MAX_CHANNELS..SUN4I_DMA_NR_MAX_CHANNELS) are
1210b096c137SEmilio López * dedicated ones
1211b096c137SEmilio López */
1212b096c137SEmilio López for (i = 0; i < SUN4I_NDMA_NR_MAX_CHANNELS; i++)
1213b096c137SEmilio López priv->pchans[i].base = priv->base +
1214b096c137SEmilio López SUN4I_NDMA_CHANNEL_REG_BASE(i);
1215b096c137SEmilio López
1216b096c137SEmilio López for (j = 0; i < SUN4I_DMA_NR_MAX_CHANNELS; i++, j++) {
1217b096c137SEmilio López priv->pchans[i].base = priv->base +
1218b096c137SEmilio López SUN4I_DDMA_CHANNEL_REG_BASE(j);
1219b096c137SEmilio López priv->pchans[i].is_dedicated = 1;
1220b096c137SEmilio López }
1221b096c137SEmilio López
1222b096c137SEmilio López for (i = 0; i < SUN4I_DMA_NR_MAX_VCHANS; i++) {
1223b096c137SEmilio López struct sun4i_dma_vchan *vchan = &priv->vchans[i];
1224b096c137SEmilio López
1225b096c137SEmilio López spin_lock_init(&vchan->vc.lock);
1226b096c137SEmilio López vchan->vc.desc_free = sun4i_dma_free_contract;
1227b096c137SEmilio López vchan_init(&vchan->vc, &priv->slave);
1228b096c137SEmilio López }
1229b096c137SEmilio López
1230b096c137SEmilio López ret = clk_prepare_enable(priv->clk);
1231b096c137SEmilio López if (ret) {
1232b096c137SEmilio López dev_err(&pdev->dev, "Couldn't enable the clock\n");
1233b096c137SEmilio López return ret;
1234b096c137SEmilio López }
1235b096c137SEmilio López
1236b096c137SEmilio López /*
1237b096c137SEmilio López * Make sure the IRQs are all disabled and accounted for. The bootloader
1238b096c137SEmilio López * likes to leave these dirty
1239b096c137SEmilio López */
1240b096c137SEmilio López writel(0, priv->base + SUN4I_DMA_IRQ_ENABLE_REG);
1241b096c137SEmilio López writel(0xFFFFFFFF, priv->base + SUN4I_DMA_IRQ_PENDING_STATUS_REG);
1242b096c137SEmilio López
1243b096c137SEmilio López ret = devm_request_irq(&pdev->dev, priv->irq, sun4i_dma_interrupt,
1244b096c137SEmilio López 0, dev_name(&pdev->dev), priv);
1245b096c137SEmilio López if (ret) {
1246b096c137SEmilio López dev_err(&pdev->dev, "Cannot request IRQ\n");
1247b096c137SEmilio López goto err_clk_disable;
1248b096c137SEmilio López }
1249b096c137SEmilio López
1250b096c137SEmilio López ret = dma_async_device_register(&priv->slave);
1251b096c137SEmilio López if (ret) {
1252b096c137SEmilio López dev_warn(&pdev->dev, "Failed to register DMA engine device\n");
1253b096c137SEmilio López goto err_clk_disable;
1254b096c137SEmilio López }
1255b096c137SEmilio López
1256b096c137SEmilio López ret = of_dma_controller_register(pdev->dev.of_node, sun4i_dma_of_xlate,
1257b096c137SEmilio López priv);
1258b096c137SEmilio López if (ret) {
1259b096c137SEmilio López dev_err(&pdev->dev, "of_dma_controller_register failed\n");
1260b096c137SEmilio López goto err_dma_unregister;
1261b096c137SEmilio López }
1262b096c137SEmilio López
1263b096c137SEmilio López dev_dbg(&pdev->dev, "Successfully probed SUN4I_DMA\n");
1264b096c137SEmilio López
1265b096c137SEmilio López return 0;
1266b096c137SEmilio López
1267b096c137SEmilio López err_dma_unregister:
1268b096c137SEmilio López dma_async_device_unregister(&priv->slave);
1269b096c137SEmilio López err_clk_disable:
1270b096c137SEmilio López clk_disable_unprepare(priv->clk);
1271b096c137SEmilio López return ret;
1272b096c137SEmilio López }
1273b096c137SEmilio López
sun4i_dma_remove(struct platform_device * pdev)1274b096c137SEmilio López static int sun4i_dma_remove(struct platform_device *pdev)
1275b096c137SEmilio López {
1276b096c137SEmilio López struct sun4i_dma_dev *priv = platform_get_drvdata(pdev);
1277b096c137SEmilio López
1278b096c137SEmilio López /* Disable IRQ so no more work is scheduled */
1279b096c137SEmilio López disable_irq(priv->irq);
1280b096c137SEmilio López
1281b096c137SEmilio López of_dma_controller_free(pdev->dev.of_node);
1282b096c137SEmilio López dma_async_device_unregister(&priv->slave);
1283b096c137SEmilio López
1284b096c137SEmilio López clk_disable_unprepare(priv->clk);
1285b096c137SEmilio López
1286b096c137SEmilio López return 0;
1287b096c137SEmilio López }
1288b096c137SEmilio López
1289b096c137SEmilio López static const struct of_device_id sun4i_dma_match[] = {
1290b096c137SEmilio López { .compatible = "allwinner,sun4i-a10-dma" },
1291b096c137SEmilio López { /* sentinel */ },
1292b096c137SEmilio López };
129394c622b2SEmilio López MODULE_DEVICE_TABLE(of, sun4i_dma_match);
1294b096c137SEmilio López
1295b096c137SEmilio López static struct platform_driver sun4i_dma_driver = {
1296b096c137SEmilio López .probe = sun4i_dma_probe,
1297b096c137SEmilio López .remove = sun4i_dma_remove,
1298b096c137SEmilio López .driver = {
1299b096c137SEmilio López .name = "sun4i-dma",
1300b096c137SEmilio López .of_match_table = sun4i_dma_match,
1301b096c137SEmilio López },
1302b096c137SEmilio López };
1303b096c137SEmilio López
1304b096c137SEmilio López module_platform_driver(sun4i_dma_driver);
1305b096c137SEmilio López
1306b096c137SEmilio López MODULE_DESCRIPTION("Allwinner A10 Dedicated DMA Controller Driver");
1307b096c137SEmilio López MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
1308b096c137SEmilio López MODULE_LICENSE("GPL");
1309