Blame view

drivers/dma/fsldma.c 34.6 KB
ea2305f6a   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
173acc7ce   Zhang Wei   dmaengine: add dr...
2
3
4
  /*
   * Freescale MPC85xx, MPC83xx DMA Engine support
   *
e2c8e425b   Li Yang   fsldma: add suppo...
5
   * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All rights reserved.
173acc7ce   Zhang Wei   dmaengine: add dr...
6
7
8
9
10
11
12
13
   *
   * Author:
   *   Zhang Wei <wei.zhang@freescale.com>, Jul 2007
   *   Ebony Zhu <ebony.zhu@freescale.com>, May 2007
   *
   * Description:
   *   DMA engine driver for Freescale MPC8540 DMA controller, which is
   *   also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc.
c2e07b3a9   Stefan Weil   Fix spelling cont...
14
   *   The support for MPC8349 DMA controller is also added.
173acc7ce   Zhang Wei   dmaengine: add dr...
15
   *
a7aea373b   Ira W. Snyder   fsldma: use PCI R...
16
17
18
19
   * This driver instructs the DMA controller to issue the PCI Read Multiple
   * command for PCI read operations, instead of using the default PCI Read Line
   * command. Please be aware that this setting may result in read pre-fetching
   * on some platforms.
173acc7ce   Zhang Wei   dmaengine: add dr...
20
21
22
23
24
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/pci.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
173acc7ce   Zhang Wei   dmaengine: add dr...
26
27
28
29
30
  #include <linux/interrupt.h>
  #include <linux/dmaengine.h>
  #include <linux/delay.h>
  #include <linux/dma-mapping.h>
  #include <linux/dmapool.h>
5af507300   Rob Herring   drivers: clean-up...
31
32
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
173acc7ce   Zhang Wei   dmaengine: add dr...
33
  #include <linux/of_platform.h>
0a5642be0   Vinod Koul   dmaengine: freesc...
34
  #include <linux/fsldma.h>
d2ebfb335   Russell King - ARM Linux   dmaengine: add pr...
35
  #include "dmaengine.h"
173acc7ce   Zhang Wei   dmaengine: add dr...
36
  #include "fsldma.h"
b158471ef   Ira Snyder   fsldma: use chann...
37
38
39
40
  #define chan_dbg(chan, fmt, arg...)					\
  	dev_dbg(chan->dev, "%s: " fmt, chan->name, ##arg)
  #define chan_err(chan, fmt, arg...)					\
  	dev_err(chan->dev, "%s: " fmt, chan->name, ##arg)
c14330417   Ira Snyder   fsldma: implement...
41

b158471ef   Ira Snyder   fsldma: use chann...
42
  static const char msg_ld_oom[] = "No free memory for link descriptor";
173acc7ce   Zhang Wei   dmaengine: add dr...
43

e8bd84df2   Ira Snyder   fsldma: move rela...
44
45
46
  /*
   * Register Helpers
   */
173acc7ce   Zhang Wei   dmaengine: add dr...
47

a1c033190   Ira Snyder   fsldma: rename fs...
48
  static void set_sr(struct fsldma_chan *chan, u32 val)
173acc7ce   Zhang Wei   dmaengine: add dr...
49
  {
a7359e762   Wen He   dmaengine: fsldma...
50
  	FSL_DMA_OUT(chan, &chan->regs->sr, val, 32);
173acc7ce   Zhang Wei   dmaengine: add dr...
51
  }
a1c033190   Ira Snyder   fsldma: rename fs...
52
  static u32 get_sr(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
53
  {
a7359e762   Wen He   dmaengine: fsldma...
54
  	return FSL_DMA_IN(chan, &chan->regs->sr, 32);
173acc7ce   Zhang Wei   dmaengine: add dr...
55
  }
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
56
57
  static void set_mr(struct fsldma_chan *chan, u32 val)
  {
a7359e762   Wen He   dmaengine: fsldma...
58
  	FSL_DMA_OUT(chan, &chan->regs->mr, val, 32);
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
59
60
61
62
  }
  
  static u32 get_mr(struct fsldma_chan *chan)
  {
a7359e762   Wen He   dmaengine: fsldma...
63
  	return FSL_DMA_IN(chan, &chan->regs->mr, 32);
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
64
  }
e8bd84df2   Ira Snyder   fsldma: move rela...
65
66
  static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
  {
a7359e762   Wen He   dmaengine: fsldma...
67
  	FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
e8bd84df2   Ira Snyder   fsldma: move rela...
68
69
70
71
  }
  
  static dma_addr_t get_cdar(struct fsldma_chan *chan)
  {
a7359e762   Wen He   dmaengine: fsldma...
72
  	return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
e8bd84df2   Ira Snyder   fsldma: move rela...
73
  }
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
74
75
  static void set_bcr(struct fsldma_chan *chan, u32 val)
  {
a7359e762   Wen He   dmaengine: fsldma...
76
  	FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32);
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
77
  }
e8bd84df2   Ira Snyder   fsldma: move rela...
78
79
  static u32 get_bcr(struct fsldma_chan *chan)
  {
a7359e762   Wen He   dmaengine: fsldma...
80
  	return FSL_DMA_IN(chan, &chan->regs->bcr, 32);
e8bd84df2   Ira Snyder   fsldma: move rela...
81
82
83
84
85
  }
  
  /*
   * Descriptor Helpers
   */
a1c033190   Ira Snyder   fsldma: rename fs...
86
  static void set_desc_cnt(struct fsldma_chan *chan,
173acc7ce   Zhang Wei   dmaengine: add dr...
87
88
  				struct fsl_dma_ld_hw *hw, u32 count)
  {
a1c033190   Ira Snyder   fsldma: rename fs...
89
  	hw->count = CPU_TO_DMA(chan, count, 32);
173acc7ce   Zhang Wei   dmaengine: add dr...
90
  }
a1c033190   Ira Snyder   fsldma: rename fs...
91
  static void set_desc_src(struct fsldma_chan *chan,
31f4306c8   Ira Snyder   fsldma: minor cod...
92
  			 struct fsl_dma_ld_hw *hw, dma_addr_t src)
173acc7ce   Zhang Wei   dmaengine: add dr...
93
94
  {
  	u64 snoop_bits;
a1c033190   Ira Snyder   fsldma: rename fs...
95
  	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
173acc7ce   Zhang Wei   dmaengine: add dr...
96
  		? ((u64)FSL_DMA_SATR_SREADTYPE_SNOOP_READ << 32) : 0;
a1c033190   Ira Snyder   fsldma: rename fs...
97
  	hw->src_addr = CPU_TO_DMA(chan, snoop_bits | src, 64);
173acc7ce   Zhang Wei   dmaengine: add dr...
98
  }
a1c033190   Ira Snyder   fsldma: rename fs...
99
  static void set_desc_dst(struct fsldma_chan *chan,
31f4306c8   Ira Snyder   fsldma: minor cod...
100
  			 struct fsl_dma_ld_hw *hw, dma_addr_t dst)
173acc7ce   Zhang Wei   dmaengine: add dr...
101
102
  {
  	u64 snoop_bits;
a1c033190   Ira Snyder   fsldma: rename fs...
103
  	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX)
173acc7ce   Zhang Wei   dmaengine: add dr...
104
  		? ((u64)FSL_DMA_DATR_DWRITETYPE_SNOOP_WRITE << 32) : 0;
a1c033190   Ira Snyder   fsldma: rename fs...
105
  	hw->dst_addr = CPU_TO_DMA(chan, snoop_bits | dst, 64);
173acc7ce   Zhang Wei   dmaengine: add dr...
106
  }
a1c033190   Ira Snyder   fsldma: rename fs...
107
  static void set_desc_next(struct fsldma_chan *chan,
31f4306c8   Ira Snyder   fsldma: minor cod...
108
  			  struct fsl_dma_ld_hw *hw, dma_addr_t next)
173acc7ce   Zhang Wei   dmaengine: add dr...
109
110
  {
  	u64 snoop_bits;
a1c033190   Ira Snyder   fsldma: rename fs...
111
  	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
173acc7ce   Zhang Wei   dmaengine: add dr...
112
  		? FSL_DMA_SNEN : 0;
a1c033190   Ira Snyder   fsldma: rename fs...
113
  	hw->next_ln_addr = CPU_TO_DMA(chan, snoop_bits | next, 64);
173acc7ce   Zhang Wei   dmaengine: add dr...
114
  }
31f4306c8   Ira Snyder   fsldma: minor cod...
115
  static void set_ld_eol(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
173acc7ce   Zhang Wei   dmaengine: add dr...
116
  {
e8bd84df2   Ira Snyder   fsldma: move rela...
117
  	u64 snoop_bits;
173acc7ce   Zhang Wei   dmaengine: add dr...
118

e8bd84df2   Ira Snyder   fsldma: move rela...
119
120
  	snoop_bits = ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
  		? FSL_DMA_SNEN : 0;
173acc7ce   Zhang Wei   dmaengine: add dr...
121

e8bd84df2   Ira Snyder   fsldma: move rela...
122
123
124
  	desc->hw.next_ln_addr = CPU_TO_DMA(chan,
  		DMA_TO_CPU(chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
  			| snoop_bits, 64);
173acc7ce   Zhang Wei   dmaengine: add dr...
125
  }
e8bd84df2   Ira Snyder   fsldma: move rela...
126
127
128
129
130
  /*
   * DMA Engine Hardware Control Helpers
   */
  
  static void dma_init(struct fsldma_chan *chan)
f79abb627   Zhang Wei   fsldma: Fix the D...
131
  {
e8bd84df2   Ira Snyder   fsldma: move rela...
132
  	/* Reset the channel */
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
133
  	set_mr(chan, 0);
e8bd84df2   Ira Snyder   fsldma: move rela...
134
135
136
137
138
  
  	switch (chan->feature & FSL_DMA_IP_MASK) {
  	case FSL_DMA_IP_85XX:
  		/* Set the channel to below modes:
  		 * EIE - Error interrupt enable
e8bd84df2   Ira Snyder   fsldma: move rela...
139
140
141
  		 * EOLNIE - End of links interrupt enable
  		 * BWC - Bandwidth sharing among channels
  		 */
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
142
143
  		set_mr(chan, FSL_DMA_MR_BWC | FSL_DMA_MR_EIE
  			| FSL_DMA_MR_EOLNIE);
e8bd84df2   Ira Snyder   fsldma: move rela...
144
145
146
147
148
149
  		break;
  	case FSL_DMA_IP_83XX:
  		/* Set the channel to below modes:
  		 * EOTIE - End-of-transfer interrupt enable
  		 * PRC_RM - PCI read multiple
  		 */
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
150
  		set_mr(chan, FSL_DMA_MR_EOTIE | FSL_DMA_MR_PRC_RM);
e8bd84df2   Ira Snyder   fsldma: move rela...
151
152
  		break;
  	}
f79abb627   Zhang Wei   fsldma: Fix the D...
153
  }
a1c033190   Ira Snyder   fsldma: rename fs...
154
  static int dma_is_idle(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
155
  {
a1c033190   Ira Snyder   fsldma: rename fs...
156
  	u32 sr = get_sr(chan);
173acc7ce   Zhang Wei   dmaengine: add dr...
157
158
  	return (!(sr & FSL_DMA_SR_CB)) || (sr & FSL_DMA_SR_CH);
  }
f04cd4070   Ira Snyder   fsldma: fix contr...
159
160
161
162
163
164
165
  /*
   * Start the DMA controller
   *
   * Preconditions:
   * - the CDAR register must point to the start descriptor
   * - the MRn[CS] bit must be cleared
   */
a1c033190   Ira Snyder   fsldma: rename fs...
166
  static void dma_start(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
167
  {
272ca6550   Ira Snyder   fsldma: reduce ke...
168
  	u32 mode;
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
169
  	mode = get_mr(chan);
272ca6550   Ira Snyder   fsldma: reduce ke...
170

f04cd4070   Ira Snyder   fsldma: fix contr...
171
  	if (chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
172
  		set_bcr(chan, 0);
f04cd4070   Ira Snyder   fsldma: fix contr...
173
174
175
  		mode |= FSL_DMA_MR_EMP_EN;
  	} else {
  		mode &= ~FSL_DMA_MR_EMP_EN;
43a1a3ed6   Ira Snyder   fsldma: do not cl...
176
  	}
173acc7ce   Zhang Wei   dmaengine: add dr...
177

f04cd4070   Ira Snyder   fsldma: fix contr...
178
  	if (chan->feature & FSL_DMA_CHAN_START_EXT) {
272ca6550   Ira Snyder   fsldma: reduce ke...
179
  		mode |= FSL_DMA_MR_EMS_EN;
f04cd4070   Ira Snyder   fsldma: fix contr...
180
181
  	} else {
  		mode &= ~FSL_DMA_MR_EMS_EN;
272ca6550   Ira Snyder   fsldma: reduce ke...
182
  		mode |= FSL_DMA_MR_CS;
f04cd4070   Ira Snyder   fsldma: fix contr...
183
  	}
173acc7ce   Zhang Wei   dmaengine: add dr...
184

ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
185
  	set_mr(chan, mode);
173acc7ce   Zhang Wei   dmaengine: add dr...
186
  }
a1c033190   Ira Snyder   fsldma: rename fs...
187
  static void dma_halt(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
188
  {
272ca6550   Ira Snyder   fsldma: reduce ke...
189
  	u32 mode;
900325a6c   Dan Williams   fsldma: fix off b...
190
  	int i;
a00ae34ac   Ira Snyder   fsldma: make halt...
191
  	/* read the mode register */
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
192
  	mode = get_mr(chan);
272ca6550   Ira Snyder   fsldma: reduce ke...
193

a00ae34ac   Ira Snyder   fsldma: make halt...
194
195
196
197
198
199
200
  	/*
  	 * The 85xx controller supports channel abort, which will stop
  	 * the current transfer. On 83xx, this bit is the transfer error
  	 * mask bit, which should not be changed.
  	 */
  	if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
  		mode |= FSL_DMA_MR_CA;
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
201
  		set_mr(chan, mode);
a00ae34ac   Ira Snyder   fsldma: make halt...
202
203
204
205
206
207
  
  		mode &= ~FSL_DMA_MR_CA;
  	}
  
  	/* stop the DMA controller */
  	mode &= ~(FSL_DMA_MR_CS | FSL_DMA_MR_EMS_EN);
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
208
  	set_mr(chan, mode);
173acc7ce   Zhang Wei   dmaengine: add dr...
209

a00ae34ac   Ira Snyder   fsldma: make halt...
210
  	/* wait for the DMA controller to become idle */
900325a6c   Dan Williams   fsldma: fix off b...
211
  	for (i = 0; i < 100; i++) {
a1c033190   Ira Snyder   fsldma: rename fs...
212
  		if (dma_is_idle(chan))
9c3a50b7d   Ira Snyder   fsldma: major cle...
213
  			return;
173acc7ce   Zhang Wei   dmaengine: add dr...
214
  		udelay(10);
900325a6c   Dan Williams   fsldma: fix off b...
215
  	}
272ca6550   Ira Snyder   fsldma: reduce ke...
216

9c3a50b7d   Ira Snyder   fsldma: major cle...
217
  	if (!dma_is_idle(chan))
b158471ef   Ira Snyder   fsldma: use chann...
218
219
  		chan_err(chan, "DMA halt timeout!
  ");
173acc7ce   Zhang Wei   dmaengine: add dr...
220
  }
173acc7ce   Zhang Wei   dmaengine: add dr...
221
222
  /**
   * fsl_chan_set_src_loop_size - Set source address hold transfer size
a1c033190   Ira Snyder   fsldma: rename fs...
223
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
224
225
226
227
228
229
230
231
   * @size     : Address loop size, 0 for disable loop
   *
   * The set source address hold transfer size. The source
   * address hold or loop transfer size is when the DMA transfer
   * data from source address (SA), if the loop size is 4, the DMA will
   * read data from SA, SA + 1, SA + 2, SA + 3, then loop back to SA,
   * SA + 1 ... and so on.
   */
a1c033190   Ira Snyder   fsldma: rename fs...
232
  static void fsl_chan_set_src_loop_size(struct fsldma_chan *chan, int size)
173acc7ce   Zhang Wei   dmaengine: add dr...
233
  {
272ca6550   Ira Snyder   fsldma: reduce ke...
234
  	u32 mode;
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
235
  	mode = get_mr(chan);
272ca6550   Ira Snyder   fsldma: reduce ke...
236

173acc7ce   Zhang Wei   dmaengine: add dr...
237
238
  	switch (size) {
  	case 0:
272ca6550   Ira Snyder   fsldma: reduce ke...
239
  		mode &= ~FSL_DMA_MR_SAHE;
173acc7ce   Zhang Wei   dmaengine: add dr...
240
241
242
243
244
  		break;
  	case 1:
  	case 2:
  	case 4:
  	case 8:
ccc077292   Thomas Breitung   dmaengine: fsldma...
245
  		mode &= ~FSL_DMA_MR_SAHTS_MASK;
272ca6550   Ira Snyder   fsldma: reduce ke...
246
  		mode |= FSL_DMA_MR_SAHE | (__ilog2(size) << 14);
173acc7ce   Zhang Wei   dmaengine: add dr...
247
248
  		break;
  	}
272ca6550   Ira Snyder   fsldma: reduce ke...
249

ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
250
  	set_mr(chan, mode);
173acc7ce   Zhang Wei   dmaengine: add dr...
251
252
253
  }
  
  /**
738f5f7e1   Ira Snyder   fsldma: rename de...
254
   * fsl_chan_set_dst_loop_size - Set destination address hold transfer size
a1c033190   Ira Snyder   fsldma: rename fs...
255
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
256
257
258
259
260
261
262
263
   * @size     : Address loop size, 0 for disable loop
   *
   * The set destination address hold transfer size. The destination
   * address hold or loop transfer size is when the DMA transfer
   * data to destination address (TA), if the loop size is 4, the DMA will
   * write data to TA, TA + 1, TA + 2, TA + 3, then loop back to TA,
   * TA + 1 ... and so on.
   */
a1c033190   Ira Snyder   fsldma: rename fs...
264
  static void fsl_chan_set_dst_loop_size(struct fsldma_chan *chan, int size)
173acc7ce   Zhang Wei   dmaengine: add dr...
265
  {
272ca6550   Ira Snyder   fsldma: reduce ke...
266
  	u32 mode;
ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
267
  	mode = get_mr(chan);
272ca6550   Ira Snyder   fsldma: reduce ke...
268

173acc7ce   Zhang Wei   dmaengine: add dr...
269
270
  	switch (size) {
  	case 0:
272ca6550   Ira Snyder   fsldma: reduce ke...
271
  		mode &= ~FSL_DMA_MR_DAHE;
173acc7ce   Zhang Wei   dmaengine: add dr...
272
273
274
275
276
  		break;
  	case 1:
  	case 2:
  	case 4:
  	case 8:
ccc077292   Thomas Breitung   dmaengine: fsldma...
277
  		mode &= ~FSL_DMA_MR_DAHTS_MASK;
272ca6550   Ira Snyder   fsldma: reduce ke...
278
  		mode |= FSL_DMA_MR_DAHE | (__ilog2(size) << 16);
173acc7ce   Zhang Wei   dmaengine: add dr...
279
280
  		break;
  	}
272ca6550   Ira Snyder   fsldma: reduce ke...
281

ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
282
  	set_mr(chan, mode);
173acc7ce   Zhang Wei   dmaengine: add dr...
283
284
285
  }
  
  /**
e6c7ecb64   Ira Snyder   fsldma: split apa...
286
   * fsl_chan_set_request_count - Set DMA Request Count for external control
a1c033190   Ira Snyder   fsldma: rename fs...
287
   * @chan : Freescale DMA channel
e6c7ecb64   Ira Snyder   fsldma: split apa...
288
289
290
291
292
293
   * @size     : Number of bytes to transfer in a single request
   *
   * The Freescale DMA channel can be controlled by the external signal DREQ#.
   * The DMA request count is how many bytes are allowed to transfer before
   * pausing the channel, after which a new assertion of DREQ# resumes channel
   * operation.
173acc7ce   Zhang Wei   dmaengine: add dr...
294
   *
e6c7ecb64   Ira Snyder   fsldma: split apa...
295
   * A size of 0 disables external pause control. The maximum size is 1024.
173acc7ce   Zhang Wei   dmaengine: add dr...
296
   */
a1c033190   Ira Snyder   fsldma: rename fs...
297
  static void fsl_chan_set_request_count(struct fsldma_chan *chan, int size)
173acc7ce   Zhang Wei   dmaengine: add dr...
298
  {
272ca6550   Ira Snyder   fsldma: reduce ke...
299
  	u32 mode;
e6c7ecb64   Ira Snyder   fsldma: split apa...
300
  	BUG_ON(size > 1024);
272ca6550   Ira Snyder   fsldma: reduce ke...
301

ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
302
  	mode = get_mr(chan);
ccc077292   Thomas Breitung   dmaengine: fsldma...
303
304
  	mode &= ~FSL_DMA_MR_BWC_MASK;
  	mode |= (__ilog2(size) << 24) & FSL_DMA_MR_BWC_MASK;
272ca6550   Ira Snyder   fsldma: reduce ke...
305

ccdce9a04   Hongbo Zhang   DMA: Freescale: u...
306
  	set_mr(chan, mode);
e6c7ecb64   Ira Snyder   fsldma: split apa...
307
  }
173acc7ce   Zhang Wei   dmaengine: add dr...
308

e6c7ecb64   Ira Snyder   fsldma: split apa...
309
310
  /**
   * fsl_chan_toggle_ext_pause - Toggle channel external pause status
a1c033190   Ira Snyder   fsldma: rename fs...
311
   * @chan : Freescale DMA channel
e6c7ecb64   Ira Snyder   fsldma: split apa...
312
313
314
315
316
317
   * @enable   : 0 is disabled, 1 is enabled.
   *
   * The Freescale DMA channel can be controlled by the external signal DREQ#.
   * The DMA Request Count feature should be used in addition to this feature
   * to set the number of bytes to transfer before pausing the channel.
   */
a1c033190   Ira Snyder   fsldma: rename fs...
318
  static void fsl_chan_toggle_ext_pause(struct fsldma_chan *chan, int enable)
e6c7ecb64   Ira Snyder   fsldma: split apa...
319
320
  {
  	if (enable)
a1c033190   Ira Snyder   fsldma: rename fs...
321
  		chan->feature |= FSL_DMA_CHAN_PAUSE_EXT;
e6c7ecb64   Ira Snyder   fsldma: split apa...
322
  	else
a1c033190   Ira Snyder   fsldma: rename fs...
323
  		chan->feature &= ~FSL_DMA_CHAN_PAUSE_EXT;
173acc7ce   Zhang Wei   dmaengine: add dr...
324
325
326
327
  }
  
  /**
   * fsl_chan_toggle_ext_start - Toggle channel external start status
a1c033190   Ira Snyder   fsldma: rename fs...
328
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
329
330
331
332
333
334
335
   * @enable   : 0 is disabled, 1 is enabled.
   *
   * If enable the external start, the channel can be started by an
   * external DMA start pin. So the dma_start() does not start the
   * transfer immediately. The DMA channel will wait for the
   * control pin asserted.
   */
a1c033190   Ira Snyder   fsldma: rename fs...
336
  static void fsl_chan_toggle_ext_start(struct fsldma_chan *chan, int enable)
173acc7ce   Zhang Wei   dmaengine: add dr...
337
338
  {
  	if (enable)
a1c033190   Ira Snyder   fsldma: rename fs...
339
  		chan->feature |= FSL_DMA_CHAN_START_EXT;
173acc7ce   Zhang Wei   dmaengine: add dr...
340
  	else
a1c033190   Ira Snyder   fsldma: rename fs...
341
  		chan->feature &= ~FSL_DMA_CHAN_START_EXT;
173acc7ce   Zhang Wei   dmaengine: add dr...
342
  }
0a5642be0   Vinod Koul   dmaengine: freesc...
343
344
345
346
347
348
349
350
351
352
353
354
355
  int fsl_dma_external_start(struct dma_chan *dchan, int enable)
  {
  	struct fsldma_chan *chan;
  
  	if (!dchan)
  		return -EINVAL;
  
  	chan = to_fsl_chan(dchan);
  
  	fsl_chan_toggle_ext_start(chan, enable);
  	return 0;
  }
  EXPORT_SYMBOL_GPL(fsl_dma_external_start);
31f4306c8   Ira Snyder   fsldma: minor cod...
356
  static void append_ld_queue(struct fsldma_chan *chan, struct fsl_desc_sw *desc)
9c3a50b7d   Ira Snyder   fsldma: major cle...
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  {
  	struct fsl_desc_sw *tail = to_fsl_desc(chan->ld_pending.prev);
  
  	if (list_empty(&chan->ld_pending))
  		goto out_splice;
  
  	/*
  	 * Add the hardware descriptor to the chain of hardware descriptors
  	 * that already exists in memory.
  	 *
  	 * This will un-set the EOL bit of the existing transaction, and the
  	 * last link in this transaction will become the EOL descriptor.
  	 */
  	set_desc_next(chan, &tail->hw, desc->async_tx.phys);
  
  	/*
  	 * Add the software descriptor and all children to the list
  	 * of pending transactions
  	 */
  out_splice:
  	list_splice_tail_init(&desc->tx_list, &chan->ld_pending);
  }
173acc7ce   Zhang Wei   dmaengine: add dr...
379
380
  static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
  {
a1c033190   Ira Snyder   fsldma: rename fs...
381
  	struct fsldma_chan *chan = to_fsl_chan(tx->chan);
eda342345   Dan Williams   fsldma: implement...
382
383
  	struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
  	struct fsl_desc_sw *child;
bbc76560d   Dan Williams   dma: fix fsldma b...
384
  	dma_cookie_t cookie = -EINVAL;
173acc7ce   Zhang Wei   dmaengine: add dr...
385

2baff5700   Hongbo Zhang   dmaengine: Freesc...
386
  	spin_lock_bh(&chan->desc_lock);
173acc7ce   Zhang Wei   dmaengine: add dr...
387

14c6a3333   Hongbo Zhang   dmaengine: Freesc...
388
389
390
391
392
393
394
395
  #ifdef CONFIG_PM
  	if (unlikely(chan->pm_state != RUNNING)) {
  		chan_dbg(chan, "cannot submit due to suspend
  ");
  		spin_unlock_bh(&chan->desc_lock);
  		return -1;
  	}
  #endif
9c3a50b7d   Ira Snyder   fsldma: major cle...
396
397
398
399
  	/*
  	 * assign cookies to all of the software descriptors
  	 * that make up this transaction
  	 */
eda342345   Dan Williams   fsldma: implement...
400
  	list_for_each_entry(child, &desc->tx_list, node) {
884485e1f   Russell King - ARM Linux   dmaengine: consol...
401
  		cookie = dma_cookie_assign(&child->async_tx);
bcfb7465c   Ira Snyder   fsldma: fix infin...
402
  	}
9c3a50b7d   Ira Snyder   fsldma: major cle...
403
  	/* put this transaction onto the tail of the pending queue */
a1c033190   Ira Snyder   fsldma: rename fs...
404
  	append_ld_queue(chan, desc);
173acc7ce   Zhang Wei   dmaengine: add dr...
405

2baff5700   Hongbo Zhang   dmaengine: Freesc...
406
  	spin_unlock_bh(&chan->desc_lock);
173acc7ce   Zhang Wei   dmaengine: add dr...
407
408
409
410
411
  
  	return cookie;
  }
  
  /**
86d19a549   Hongbo Zhang   DMA: Freescale: a...
412
413
414
415
416
417
418
419
420
421
422
423
424
425
   * fsl_dma_free_descriptor - Free descriptor from channel's DMA pool.
   * @chan : Freescale DMA channel
   * @desc: descriptor to be freed
   */
  static void fsl_dma_free_descriptor(struct fsldma_chan *chan,
  		struct fsl_desc_sw *desc)
  {
  	list_del(&desc->node);
  	chan_dbg(chan, "LD %p free
  ", desc);
  	dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
  }
  
  /**
173acc7ce   Zhang Wei   dmaengine: add dr...
426
   * fsl_dma_alloc_descriptor - Allocate descriptor from channel's DMA pool.
a1c033190   Ira Snyder   fsldma: rename fs...
427
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
428
429
430
   *
   * Return - The descriptor allocated. NULL for failed.
   */
31f4306c8   Ira Snyder   fsldma: minor cod...
431
  static struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
432
  {
9c3a50b7d   Ira Snyder   fsldma: major cle...
433
  	struct fsl_desc_sw *desc;
173acc7ce   Zhang Wei   dmaengine: add dr...
434
  	dma_addr_t pdesc;
9c3a50b7d   Ira Snyder   fsldma: major cle...
435

437645572   Julia Lawall   dmaengine: fsldma...
436
  	desc = dma_pool_zalloc(chan->desc_pool, GFP_ATOMIC, &pdesc);
9c3a50b7d   Ira Snyder   fsldma: major cle...
437
  	if (!desc) {
b158471ef   Ira Snyder   fsldma: use chann...
438
439
  		chan_dbg(chan, "out of memory for link descriptor
  ");
9c3a50b7d   Ira Snyder   fsldma: major cle...
440
  		return NULL;
173acc7ce   Zhang Wei   dmaengine: add dr...
441
  	}
9c3a50b7d   Ira Snyder   fsldma: major cle...
442
443
444
445
  	INIT_LIST_HEAD(&desc->tx_list);
  	dma_async_tx_descriptor_init(&desc->async_tx, &chan->common);
  	desc->async_tx.tx_submit = fsl_dma_tx_submit;
  	desc->async_tx.phys = pdesc;
0ab09c368   Ira Snyder   fsldma: improve l...
446
447
  	chan_dbg(chan, "LD %p allocated
  ", desc);
0ab09c368   Ira Snyder   fsldma: improve l...
448

9c3a50b7d   Ira Snyder   fsldma: major cle...
449
  	return desc;
173acc7ce   Zhang Wei   dmaengine: add dr...
450
  }
173acc7ce   Zhang Wei   dmaengine: add dr...
451
  /**
43452fadd   Hongbo Zhang   dmaengine: Freesc...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
   * fsldma_clean_completed_descriptor - free all descriptors which
   * has been completed and acked
   * @chan: Freescale DMA channel
   *
   * This function is used on all completed and acked descriptors.
   * All descriptors should only be freed in this function.
   */
  static void fsldma_clean_completed_descriptor(struct fsldma_chan *chan)
  {
  	struct fsl_desc_sw *desc, *_desc;
  
  	/* Run the callback for each descriptor, in order */
  	list_for_each_entry_safe(desc, _desc, &chan->ld_completed, node)
  		if (async_tx_test_ack(&desc->async_tx))
  			fsl_dma_free_descriptor(chan, desc);
  }
  
  /**
   * fsldma_run_tx_complete_actions - cleanup a single link descriptor
   * @chan: Freescale DMA channel
   * @desc: descriptor to cleanup and free
   * @cookie: Freescale DMA transaction identifier
   *
   * This function is used on a descriptor which has been executed by the DMA
   * controller. It will run any callbacks, submit any dependencies.
   */
  static dma_cookie_t fsldma_run_tx_complete_actions(struct fsldma_chan *chan,
  		struct fsl_desc_sw *desc, dma_cookie_t cookie)
  {
  	struct dma_async_tx_descriptor *txd = &desc->async_tx;
  	dma_cookie_t ret = cookie;
  
  	BUG_ON(txd->cookie < 0);
  
  	if (txd->cookie > 0) {
  		ret = txd->cookie;
9b335978f   Dave Jiang   dmaengine: fsldma...
488
  		dma_descriptor_unmap(txd);
43452fadd   Hongbo Zhang   dmaengine: Freesc...
489
  		/* Run the link descriptor callback function */
af1a5a511   Dave Jiang   dmaengine: fsldma...
490
  		dmaengine_desc_get_callback_invoke(txd, NULL);
43452fadd   Hongbo Zhang   dmaengine: Freesc...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
  	}
  
  	/* Run any dependencies */
  	dma_run_dependencies(txd);
  
  	return ret;
  }
  
  /**
   * fsldma_clean_running_descriptor - move the completed descriptor from
   * ld_running to ld_completed
   * @chan: Freescale DMA channel
   * @desc: the descriptor which is completed
   *
   * Free the descriptor directly if acked by async_tx api, or move it to
   * queue ld_completed.
   */
  static void fsldma_clean_running_descriptor(struct fsldma_chan *chan,
  		struct fsl_desc_sw *desc)
  {
  	/* Remove from the list of transactions */
  	list_del(&desc->node);
  
  	/*
  	 * the client is allowed to attach dependent operations
  	 * until 'ack' is set
  	 */
  	if (!async_tx_test_ack(&desc->async_tx)) {
  		/*
  		 * Move this descriptor to the list of descriptors which is
  		 * completed, but still awaiting the 'ack' bit to be set.
  		 */
  		list_add_tail(&desc->node, &chan->ld_completed);
  		return;
  	}
  
  	dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys);
  }
  
  /**
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
   * fsl_chan_xfer_ld_queue - transfer any pending transactions
   * @chan : Freescale DMA channel
   *
   * HARDWARE STATE: idle
   * LOCKING: must hold chan->desc_lock
   */
  static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan)
  {
  	struct fsl_desc_sw *desc;
  
  	/*
  	 * If the list of pending descriptors is empty, then we
  	 * don't need to do any work at all
  	 */
  	if (list_empty(&chan->ld_pending)) {
  		chan_dbg(chan, "no pending LDs
  ");
  		return;
  	}
  
  	/*
  	 * The DMA controller is not idle, which means that the interrupt
  	 * handler will start any queued transactions when it runs after
  	 * this transaction finishes
  	 */
  	if (!chan->idle) {
  		chan_dbg(chan, "DMA controller still busy
  ");
  		return;
  	}
  
  	/*
  	 * If there are some link descriptors which have not been
  	 * transferred, we need to start the controller
  	 */
  
  	/*
  	 * Move all elements from the queue of pending transactions
  	 * onto the list of running transactions
  	 */
  	chan_dbg(chan, "idle, starting controller
  ");
  	desc = list_first_entry(&chan->ld_pending, struct fsl_desc_sw, node);
  	list_splice_tail_init(&chan->ld_pending, &chan->ld_running);
  
  	/*
  	 * The 85xx DMA controller doesn't clear the channel start bit
  	 * automatically at the end of a transfer. Therefore we must clear
  	 * it in software before starting the transfer.
  	 */
  	if ((chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
  		u32 mode;
  
  		mode = get_mr(chan);
  		mode &= ~FSL_DMA_MR_CS;
  		set_mr(chan, mode);
  	}
  
  	/*
  	 * Program the descriptor's address into the DMA controller,
  	 * then start the DMA transaction
  	 */
  	set_cdar(chan, desc->async_tx.phys);
  	get_cdar(chan);
  
  	dma_start(chan);
  	chan->idle = false;
  }
  
  /**
43452fadd   Hongbo Zhang   dmaengine: Freesc...
601
602
   * fsldma_cleanup_descriptors - cleanup link descriptors which are completed
   * and move them to ld_completed to free until flag 'ack' is set
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
603
   * @chan: Freescale DMA channel
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
604
   *
43452fadd   Hongbo Zhang   dmaengine: Freesc...
605
606
607
   * This function is used on descriptors which have been executed by the DMA
   * controller. It will run any callbacks, submit any dependencies, then
   * free these descriptors if flag 'ack' is set.
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
608
   */
43452fadd   Hongbo Zhang   dmaengine: Freesc...
609
  static void fsldma_cleanup_descriptors(struct fsldma_chan *chan)
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
610
  {
43452fadd   Hongbo Zhang   dmaengine: Freesc...
611
612
613
614
  	struct fsl_desc_sw *desc, *_desc;
  	dma_cookie_t cookie = 0;
  	dma_addr_t curr_phys = get_cdar(chan);
  	int seen_current = 0;
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
615

43452fadd   Hongbo Zhang   dmaengine: Freesc...
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  	fsldma_clean_completed_descriptor(chan);
  
  	/* Run the callback for each descriptor, in order */
  	list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) {
  		/*
  		 * do not advance past the current descriptor loaded into the
  		 * hardware channel, subsequent descriptors are either in
  		 * process or have not been submitted
  		 */
  		if (seen_current)
  			break;
  
  		/*
  		 * stop the search if we reach the current descriptor and the
  		 * channel is busy
  		 */
  		if (desc->async_tx.phys == curr_phys) {
  			seen_current = 1;
  			if (!dma_is_idle(chan))
  				break;
  		}
  
  		cookie = fsldma_run_tx_complete_actions(chan, desc, cookie);
  
  		fsldma_clean_running_descriptor(chan, desc);
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
641
  	}
43452fadd   Hongbo Zhang   dmaengine: Freesc...
642
643
644
645
646
647
648
  	/*
  	 * Start any pending transactions automatically
  	 *
  	 * In the ideal case, we keep the DMA controller busy while we go
  	 * ahead and free the descriptors below.
  	 */
  	fsl_chan_xfer_ld_queue(chan);
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
649

43452fadd   Hongbo Zhang   dmaengine: Freesc...
650
651
  	if (cookie > 0)
  		chan->common.completed_cookie = cookie;
2a5ecb791   Hongbo Zhang   DMA: Freescale: m...
652
653
654
  }
  
  /**
173acc7ce   Zhang Wei   dmaengine: add dr...
655
   * fsl_dma_alloc_chan_resources - Allocate resources for DMA channel.
a1c033190   Ira Snyder   fsldma: rename fs...
656
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
657
658
659
660
661
   *
   * This function will create a dma pool for descriptor allocation.
   *
   * Return - The number of descriptors allocated.
   */
a1c033190   Ira Snyder   fsldma: rename fs...
662
  static int fsl_dma_alloc_chan_resources(struct dma_chan *dchan)
173acc7ce   Zhang Wei   dmaengine: add dr...
663
  {
a1c033190   Ira Snyder   fsldma: rename fs...
664
  	struct fsldma_chan *chan = to_fsl_chan(dchan);
77cd62e80   Timur Tabi   fsldma: allow Fre...
665
666
  
  	/* Has this channel already been allocated? */
a1c033190   Ira Snyder   fsldma: rename fs...
667
  	if (chan->desc_pool)
77cd62e80   Timur Tabi   fsldma: allow Fre...
668
  		return 1;
173acc7ce   Zhang Wei   dmaengine: add dr...
669

9c3a50b7d   Ira Snyder   fsldma: major cle...
670
671
  	/*
  	 * We need the descriptor to be aligned to 32bytes
173acc7ce   Zhang Wei   dmaengine: add dr...
672
673
  	 * for meeting FSL DMA specification requirement.
  	 */
b158471ef   Ira Snyder   fsldma: use chann...
674
  	chan->desc_pool = dma_pool_create(chan->name, chan->dev,
9c3a50b7d   Ira Snyder   fsldma: major cle...
675
676
  					  sizeof(struct fsl_desc_sw),
  					  __alignof__(struct fsl_desc_sw), 0);
a1c033190   Ira Snyder   fsldma: rename fs...
677
  	if (!chan->desc_pool) {
b158471ef   Ira Snyder   fsldma: use chann...
678
679
  		chan_err(chan, "unable to allocate descriptor pool
  ");
9c3a50b7d   Ira Snyder   fsldma: major cle...
680
  		return -ENOMEM;
173acc7ce   Zhang Wei   dmaengine: add dr...
681
  	}
9c3a50b7d   Ira Snyder   fsldma: major cle...
682
  	/* there is at least one descriptor free to be allocated */
173acc7ce   Zhang Wei   dmaengine: add dr...
683
684
685
686
  	return 1;
  }
  
  /**
9c3a50b7d   Ira Snyder   fsldma: major cle...
687
688
689
690
691
692
693
694
695
696
   * fsldma_free_desc_list - Free all descriptors in a queue
   * @chan: Freescae DMA channel
   * @list: the list to free
   *
   * LOCKING: must hold chan->desc_lock
   */
  static void fsldma_free_desc_list(struct fsldma_chan *chan,
  				  struct list_head *list)
  {
  	struct fsl_desc_sw *desc, *_desc;
86d19a549   Hongbo Zhang   DMA: Freescale: a...
697
698
  	list_for_each_entry_safe(desc, _desc, list, node)
  		fsl_dma_free_descriptor(chan, desc);
9c3a50b7d   Ira Snyder   fsldma: major cle...
699
700
701
702
703
704
  }
  
  static void fsldma_free_desc_list_reverse(struct fsldma_chan *chan,
  					  struct list_head *list)
  {
  	struct fsl_desc_sw *desc, *_desc;
86d19a549   Hongbo Zhang   DMA: Freescale: a...
705
706
  	list_for_each_entry_safe_reverse(desc, _desc, list, node)
  		fsl_dma_free_descriptor(chan, desc);
9c3a50b7d   Ira Snyder   fsldma: major cle...
707
708
709
  }
  
  /**
173acc7ce   Zhang Wei   dmaengine: add dr...
710
   * fsl_dma_free_chan_resources - Free all resources of the channel.
a1c033190   Ira Snyder   fsldma: rename fs...
711
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
712
   */
a1c033190   Ira Snyder   fsldma: rename fs...
713
  static void fsl_dma_free_chan_resources(struct dma_chan *dchan)
173acc7ce   Zhang Wei   dmaengine: add dr...
714
  {
a1c033190   Ira Snyder   fsldma: rename fs...
715
  	struct fsldma_chan *chan = to_fsl_chan(dchan);
173acc7ce   Zhang Wei   dmaengine: add dr...
716

b158471ef   Ira Snyder   fsldma: use chann...
717
718
  	chan_dbg(chan, "free all channel resources
  ");
2baff5700   Hongbo Zhang   dmaengine: Freesc...
719
  	spin_lock_bh(&chan->desc_lock);
43452fadd   Hongbo Zhang   dmaengine: Freesc...
720
  	fsldma_cleanup_descriptors(chan);
9c3a50b7d   Ira Snyder   fsldma: major cle...
721
722
  	fsldma_free_desc_list(chan, &chan->ld_pending);
  	fsldma_free_desc_list(chan, &chan->ld_running);
43452fadd   Hongbo Zhang   dmaengine: Freesc...
723
  	fsldma_free_desc_list(chan, &chan->ld_completed);
2baff5700   Hongbo Zhang   dmaengine: Freesc...
724
  	spin_unlock_bh(&chan->desc_lock);
77cd62e80   Timur Tabi   fsldma: allow Fre...
725

9c3a50b7d   Ira Snyder   fsldma: major cle...
726
  	dma_pool_destroy(chan->desc_pool);
a1c033190   Ira Snyder   fsldma: rename fs...
727
  	chan->desc_pool = NULL;
173acc7ce   Zhang Wei   dmaengine: add dr...
728
  }
2187c269a   Zhang Wei   fsldma: Add devic...
729
  static struct dma_async_tx_descriptor *
31f4306c8   Ira Snyder   fsldma: minor cod...
730
731
  fsl_dma_prep_memcpy(struct dma_chan *dchan,
  	dma_addr_t dma_dst, dma_addr_t dma_src,
173acc7ce   Zhang Wei   dmaengine: add dr...
732
733
  	size_t len, unsigned long flags)
  {
a1c033190   Ira Snyder   fsldma: rename fs...
734
  	struct fsldma_chan *chan;
173acc7ce   Zhang Wei   dmaengine: add dr...
735
736
  	struct fsl_desc_sw *first = NULL, *prev = NULL, *new;
  	size_t copy;
173acc7ce   Zhang Wei   dmaengine: add dr...
737

a1c033190   Ira Snyder   fsldma: rename fs...
738
  	if (!dchan)
173acc7ce   Zhang Wei   dmaengine: add dr...
739
740
741
742
  		return NULL;
  
  	if (!len)
  		return NULL;
a1c033190   Ira Snyder   fsldma: rename fs...
743
  	chan = to_fsl_chan(dchan);
173acc7ce   Zhang Wei   dmaengine: add dr...
744
745
746
747
  
  	do {
  
  		/* Allocate the link descriptor from DMA pool */
a1c033190   Ira Snyder   fsldma: rename fs...
748
  		new = fsl_dma_alloc_descriptor(chan);
173acc7ce   Zhang Wei   dmaengine: add dr...
749
  		if (!new) {
b158471ef   Ira Snyder   fsldma: use chann...
750
751
  			chan_err(chan, "%s
  ", msg_ld_oom);
2e077f8e8   Ira Snyder   fsldma: fix memor...
752
  			goto fail;
173acc7ce   Zhang Wei   dmaengine: add dr...
753
  		}
173acc7ce   Zhang Wei   dmaengine: add dr...
754

56822843f   Zhang Wei   fsldma: Fix fsldm...
755
  		copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT);
173acc7ce   Zhang Wei   dmaengine: add dr...
756

a1c033190   Ira Snyder   fsldma: rename fs...
757
758
759
  		set_desc_cnt(chan, &new->hw, copy);
  		set_desc_src(chan, &new->hw, dma_src);
  		set_desc_dst(chan, &new->hw, dma_dst);
173acc7ce   Zhang Wei   dmaengine: add dr...
760
761
762
763
  
  		if (!first)
  			first = new;
  		else
a1c033190   Ira Snyder   fsldma: rename fs...
764
  			set_desc_next(chan, &prev->hw, new->async_tx.phys);
173acc7ce   Zhang Wei   dmaengine: add dr...
765
766
  
  		new->async_tx.cookie = 0;
636bdeaa1   Dan Williams   dmaengine: ack to...
767
  		async_tx_ack(&new->async_tx);
173acc7ce   Zhang Wei   dmaengine: add dr...
768
769
770
771
  
  		prev = new;
  		len -= copy;
  		dma_src += copy;
738f5f7e1   Ira Snyder   fsldma: rename de...
772
  		dma_dst += copy;
173acc7ce   Zhang Wei   dmaengine: add dr...
773
774
  
  		/* Insert the link descriptor to the LD ring */
eda342345   Dan Williams   fsldma: implement...
775
  		list_add_tail(&new->node, &first->tx_list);
173acc7ce   Zhang Wei   dmaengine: add dr...
776
  	} while (len);
636bdeaa1   Dan Williams   dmaengine: ack to...
777
  	new->async_tx.flags = flags; /* client is in control of this ack */
173acc7ce   Zhang Wei   dmaengine: add dr...
778
  	new->async_tx.cookie = -EBUSY;
31f4306c8   Ira Snyder   fsldma: minor cod...
779
  	/* Set End-of-link to the last link descriptor of new list */
a1c033190   Ira Snyder   fsldma: rename fs...
780
  	set_ld_eol(chan, new);
173acc7ce   Zhang Wei   dmaengine: add dr...
781

2e077f8e8   Ira Snyder   fsldma: fix memor...
782
783
784
785
786
  	return &first->async_tx;
  
  fail:
  	if (!first)
  		return NULL;
9c3a50b7d   Ira Snyder   fsldma: major cle...
787
  	fsldma_free_desc_list_reverse(chan, &first->tx_list);
2e077f8e8   Ira Snyder   fsldma: fix memor...
788
  	return NULL;
173acc7ce   Zhang Wei   dmaengine: add dr...
789
  }
b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
790
  static int fsl_dma_device_terminate_all(struct dma_chan *dchan)
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
791
  {
a1c033190   Ira Snyder   fsldma: rename fs...
792
  	struct fsldma_chan *chan;
c3635c78e   Linus Walleij   DMAENGINE: generi...
793

a1c033190   Ira Snyder   fsldma: rename fs...
794
  	if (!dchan)
c3635c78e   Linus Walleij   DMAENGINE: generi...
795
  		return -EINVAL;
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
796

a1c033190   Ira Snyder   fsldma: rename fs...
797
  	chan = to_fsl_chan(dchan);
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
798

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
799
  	spin_lock_bh(&chan->desc_lock);
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
800

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
801
802
  	/* Halt the DMA engine */
  	dma_halt(chan);
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
803

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
804
805
806
807
808
  	/* Remove and free all of the descriptors in the LD queue */
  	fsldma_free_desc_list(chan, &chan->ld_pending);
  	fsldma_free_desc_list(chan, &chan->ld_running);
  	fsldma_free_desc_list(chan, &chan->ld_completed);
  	chan->idle = true;
968f19ae8   Ira Snyder   fsldma: improved ...
809

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
810
811
812
  	spin_unlock_bh(&chan->desc_lock);
  	return 0;
  }
968f19ae8   Ira Snyder   fsldma: improved ...
813

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
814
815
816
817
818
  static int fsl_dma_device_config(struct dma_chan *dchan,
  				 struct dma_slave_config *config)
  {
  	struct fsldma_chan *chan;
  	int size;
968f19ae8   Ira Snyder   fsldma: improved ...
819

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
820
821
  	if (!dchan)
  		return -EINVAL;
968f19ae8   Ira Snyder   fsldma: improved ...
822

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
823
  	chan = to_fsl_chan(dchan);
968f19ae8   Ira Snyder   fsldma: improved ...
824

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
825
826
  	/* make sure the channel supports setting burst size */
  	if (!chan->set_request_count)
968f19ae8   Ira Snyder   fsldma: improved ...
827
  		return -ENXIO;
c3635c78e   Linus Walleij   DMAENGINE: generi...
828

b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
829
830
831
832
833
834
835
  	/* we set the controller burst size depending on direction */
  	if (config->direction == DMA_MEM_TO_DEV)
  		size = config->dst_addr_width * config->dst_maxburst;
  	else
  		size = config->src_addr_width * config->src_maxburst;
  
  	chan->set_request_count(chan, size);
c3635c78e   Linus Walleij   DMAENGINE: generi...
836
  	return 0;
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
837
  }
b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
838

bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
839
  /**
173acc7ce   Zhang Wei   dmaengine: add dr...
840
   * fsl_dma_memcpy_issue_pending - Issue the DMA start command
a1c033190   Ira Snyder   fsldma: rename fs...
841
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
842
   */
a1c033190   Ira Snyder   fsldma: rename fs...
843
  static void fsl_dma_memcpy_issue_pending(struct dma_chan *dchan)
173acc7ce   Zhang Wei   dmaengine: add dr...
844
  {
a1c033190   Ira Snyder   fsldma: rename fs...
845
  	struct fsldma_chan *chan = to_fsl_chan(dchan);
dc8d40915   Ira Snyder   fsldma: reduce lo...
846

2baff5700   Hongbo Zhang   dmaengine: Freesc...
847
  	spin_lock_bh(&chan->desc_lock);
a1c033190   Ira Snyder   fsldma: rename fs...
848
  	fsl_chan_xfer_ld_queue(chan);
2baff5700   Hongbo Zhang   dmaengine: Freesc...
849
  	spin_unlock_bh(&chan->desc_lock);
173acc7ce   Zhang Wei   dmaengine: add dr...
850
  }
173acc7ce   Zhang Wei   dmaengine: add dr...
851
  /**
079344818   Linus Walleij   DMAENGINE: generi...
852
   * fsl_tx_status - Determine the DMA status
a1c033190   Ira Snyder   fsldma: rename fs...
853
   * @chan : Freescale DMA channel
173acc7ce   Zhang Wei   dmaengine: add dr...
854
   */
079344818   Linus Walleij   DMAENGINE: generi...
855
  static enum dma_status fsl_tx_status(struct dma_chan *dchan,
173acc7ce   Zhang Wei   dmaengine: add dr...
856
  					dma_cookie_t cookie,
079344818   Linus Walleij   DMAENGINE: generi...
857
  					struct dma_tx_state *txstate)
173acc7ce   Zhang Wei   dmaengine: add dr...
858
  {
43452fadd   Hongbo Zhang   dmaengine: Freesc...
859
860
861
862
863
864
865
866
867
868
  	struct fsldma_chan *chan = to_fsl_chan(dchan);
  	enum dma_status ret;
  
  	ret = dma_cookie_status(dchan, cookie, txstate);
  	if (ret == DMA_COMPLETE)
  		return ret;
  
  	spin_lock_bh(&chan->desc_lock);
  	fsldma_cleanup_descriptors(chan);
  	spin_unlock_bh(&chan->desc_lock);
9b0b0bdc6   Andy Shevchenko   fsldma: remove us...
869
  	return dma_cookie_status(dchan, cookie, txstate);
173acc7ce   Zhang Wei   dmaengine: add dr...
870
  }
d3f620b2c   Ira Snyder   fsldma: simplify ...
871
872
873
  /*----------------------------------------------------------------------------*/
  /* Interrupt Handling                                                         */
  /*----------------------------------------------------------------------------*/
e7a29151d   Ira Snyder   fsldma: clean up ...
874
  static irqreturn_t fsldma_chan_irq(int irq, void *data)
173acc7ce   Zhang Wei   dmaengine: add dr...
875
  {
a1c033190   Ira Snyder   fsldma: rename fs...
876
  	struct fsldma_chan *chan = data;
a1c033190   Ira Snyder   fsldma: rename fs...
877
  	u32 stat;
173acc7ce   Zhang Wei   dmaengine: add dr...
878

9c3a50b7d   Ira Snyder   fsldma: major cle...
879
  	/* save and clear the status register */
a1c033190   Ira Snyder   fsldma: rename fs...
880
  	stat = get_sr(chan);
9c3a50b7d   Ira Snyder   fsldma: major cle...
881
  	set_sr(chan, stat);
b158471ef   Ira Snyder   fsldma: use chann...
882
883
  	chan_dbg(chan, "irq: stat = 0x%x
  ", stat);
173acc7ce   Zhang Wei   dmaengine: add dr...
884

f04cd4070   Ira Snyder   fsldma: fix contr...
885
  	/* check that this was really our device */
173acc7ce   Zhang Wei   dmaengine: add dr...
886
887
888
889
890
  	stat &= ~(FSL_DMA_SR_CB | FSL_DMA_SR_CH);
  	if (!stat)
  		return IRQ_NONE;
  
  	if (stat & FSL_DMA_SR_TE)
b158471ef   Ira Snyder   fsldma: use chann...
891
892
  		chan_err(chan, "Transfer Error!
  ");
173acc7ce   Zhang Wei   dmaengine: add dr...
893

9c3a50b7d   Ira Snyder   fsldma: major cle...
894
895
  	/*
  	 * Programming Error
f79abb627   Zhang Wei   fsldma: Fix the D...
896
  	 * The DMA_INTERRUPT async_tx is a NULL transfer, which will
d73111c6d   Masanari Iida   dma: fix comments
897
  	 * trigger a PE interrupt.
f79abb627   Zhang Wei   fsldma: Fix the D...
898
899
  	 */
  	if (stat & FSL_DMA_SR_PE) {
b158471ef   Ira Snyder   fsldma: use chann...
900
901
  		chan_dbg(chan, "irq: Programming Error INT
  ");
f79abb627   Zhang Wei   fsldma: Fix the D...
902
  		stat &= ~FSL_DMA_SR_PE;
f04cd4070   Ira Snyder   fsldma: fix contr...
903
904
905
  		if (get_bcr(chan) != 0)
  			chan_err(chan, "Programming Error!
  ");
1c62979ed   Zhang Wei   fsldma: Split the...
906
  	}
9c3a50b7d   Ira Snyder   fsldma: major cle...
907
908
  	/*
  	 * For MPC8349, EOCDI event need to update cookie
1c62979ed   Zhang Wei   fsldma: Split the...
909
910
911
  	 * and start the next transfer if it exist.
  	 */
  	if (stat & FSL_DMA_SR_EOCDI) {
b158471ef   Ira Snyder   fsldma: use chann...
912
913
  		chan_dbg(chan, "irq: End-of-Chain link INT
  ");
1c62979ed   Zhang Wei   fsldma: Split the...
914
  		stat &= ~FSL_DMA_SR_EOCDI;
173acc7ce   Zhang Wei   dmaengine: add dr...
915
  	}
9c3a50b7d   Ira Snyder   fsldma: major cle...
916
917
  	/*
  	 * If it current transfer is the end-of-transfer,
173acc7ce   Zhang Wei   dmaengine: add dr...
918
919
920
  	 * we should clear the Channel Start bit for
  	 * prepare next transfer.
  	 */
1c62979ed   Zhang Wei   fsldma: Split the...
921
  	if (stat & FSL_DMA_SR_EOLNI) {
b158471ef   Ira Snyder   fsldma: use chann...
922
923
  		chan_dbg(chan, "irq: End-of-link INT
  ");
173acc7ce   Zhang Wei   dmaengine: add dr...
924
  		stat &= ~FSL_DMA_SR_EOLNI;
173acc7ce   Zhang Wei   dmaengine: add dr...
925
  	}
f04cd4070   Ira Snyder   fsldma: fix contr...
926
927
928
929
930
931
  	/* check that the DMA controller is really idle */
  	if (!dma_is_idle(chan))
  		chan_err(chan, "irq: controller not idle!
  ");
  
  	/* check that we handled all of the bits */
173acc7ce   Zhang Wei   dmaengine: add dr...
932
  	if (stat)
f04cd4070   Ira Snyder   fsldma: fix contr...
933
934
  		chan_err(chan, "irq: unhandled sr 0x%08x
  ", stat);
173acc7ce   Zhang Wei   dmaengine: add dr...
935

f04cd4070   Ira Snyder   fsldma: fix contr...
936
937
938
939
940
  	/*
  	 * Schedule the tasklet to handle all cleanup of the current
  	 * transaction. It will start a new transaction if there is
  	 * one pending.
  	 */
a1c033190   Ira Snyder   fsldma: rename fs...
941
  	tasklet_schedule(&chan->tasklet);
f04cd4070   Ira Snyder   fsldma: fix contr...
942
943
  	chan_dbg(chan, "irq: Exit
  ");
173acc7ce   Zhang Wei   dmaengine: add dr...
944
945
  	return IRQ_HANDLED;
  }
59cd81876   Allen Pais   dmaengine: fsl: c...
946
  static void dma_do_tasklet(struct tasklet_struct *t)
d3f620b2c   Ira Snyder   fsldma: simplify ...
947
  {
59cd81876   Allen Pais   dmaengine: fsl: c...
948
  	struct fsldma_chan *chan = from_tasklet(chan, t, tasklet);
f04cd4070   Ira Snyder   fsldma: fix contr...
949
950
951
  
  	chan_dbg(chan, "tasklet entry
  ");
1297b647c   Barry Song   dmaengine: fsldma...
952
  	spin_lock(&chan->desc_lock);
dc8d40915   Ira Snyder   fsldma: reduce lo...
953

dc8d40915   Ira Snyder   fsldma: reduce lo...
954
  	/* the hardware is now idle and ready for more */
f04cd4070   Ira Snyder   fsldma: fix contr...
955
  	chan->idle = true;
f04cd4070   Ira Snyder   fsldma: fix contr...
956

43452fadd   Hongbo Zhang   dmaengine: Freesc...
957
958
  	/* Run all cleanup for descriptors which have been completed */
  	fsldma_cleanup_descriptors(chan);
dc8d40915   Ira Snyder   fsldma: reduce lo...
959

1297b647c   Barry Song   dmaengine: fsldma...
960
  	spin_unlock(&chan->desc_lock);
dc8d40915   Ira Snyder   fsldma: reduce lo...
961

f04cd4070   Ira Snyder   fsldma: fix contr...
962
963
  	chan_dbg(chan, "tasklet exit
  ");
d3f620b2c   Ira Snyder   fsldma: simplify ...
964
965
966
  }
  
  static irqreturn_t fsldma_ctrl_irq(int irq, void *data)
173acc7ce   Zhang Wei   dmaengine: add dr...
967
  {
a4f56d4b1   Ira Snyder   fsldma: rename st...
968
  	struct fsldma_device *fdev = data;
d3f620b2c   Ira Snyder   fsldma: simplify ...
969
970
971
972
  	struct fsldma_chan *chan;
  	unsigned int handled = 0;
  	u32 gsr, mask;
  	int i;
173acc7ce   Zhang Wei   dmaengine: add dr...
973

e7a29151d   Ira Snyder   fsldma: clean up ...
974
  	gsr = (fdev->feature & FSL_DMA_BIG_ENDIAN) ? in_be32(fdev->regs)
d3f620b2c   Ira Snyder   fsldma: simplify ...
975
976
977
978
  						   : in_le32(fdev->regs);
  	mask = 0xff000000;
  	dev_dbg(fdev->dev, "IRQ: gsr 0x%.8x
  ", gsr);
173acc7ce   Zhang Wei   dmaengine: add dr...
979

d3f620b2c   Ira Snyder   fsldma: simplify ...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
  
  		if (gsr & mask) {
  			dev_dbg(fdev->dev, "IRQ: chan %d
  ", chan->id);
  			fsldma_chan_irq(irq, chan);
  			handled++;
  		}
  
  		gsr &= ~mask;
  		mask >>= 8;
  	}
  
  	return IRQ_RETVAL(handled);
173acc7ce   Zhang Wei   dmaengine: add dr...
997
  }
d3f620b2c   Ira Snyder   fsldma: simplify ...
998
  static void fsldma_free_irqs(struct fsldma_device *fdev)
173acc7ce   Zhang Wei   dmaengine: add dr...
999
  {
d3f620b2c   Ira Snyder   fsldma: simplify ...
1000
1001
  	struct fsldma_chan *chan;
  	int i;
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1002
  	if (fdev->irq) {
d3f620b2c   Ira Snyder   fsldma: simplify ...
1003
1004
1005
1006
1007
1008
1009
1010
  		dev_dbg(fdev->dev, "free per-controller IRQ
  ");
  		free_irq(fdev->irq, fdev);
  		return;
  	}
  
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
  		chan = fdev->chan[i];
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1011
  		if (chan && chan->irq) {
b158471ef   Ira Snyder   fsldma: use chann...
1012
1013
  			chan_dbg(chan, "free per-channel IRQ
  ");
d3f620b2c   Ira Snyder   fsldma: simplify ...
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
  			free_irq(chan->irq, chan);
  		}
  	}
  }
  
  static int fsldma_request_irqs(struct fsldma_device *fdev)
  {
  	struct fsldma_chan *chan;
  	int ret;
  	int i;
  
  	/* if we have a per-controller IRQ, use that */
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1026
  	if (fdev->irq) {
d3f620b2c   Ira Snyder   fsldma: simplify ...
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
  		dev_dbg(fdev->dev, "request per-controller IRQ
  ");
  		ret = request_irq(fdev->irq, fsldma_ctrl_irq, IRQF_SHARED,
  				  "fsldma-controller", fdev);
  		return ret;
  	}
  
  	/* no per-controller IRQ, use the per-channel IRQs */
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1039
  		if (!chan->irq) {
b158471ef   Ira Snyder   fsldma: use chann...
1040
1041
  			chan_err(chan, "interrupts property missing in device tree
  ");
d3f620b2c   Ira Snyder   fsldma: simplify ...
1042
1043
1044
  			ret = -ENODEV;
  			goto out_unwind;
  		}
b158471ef   Ira Snyder   fsldma: use chann...
1045
1046
  		chan_dbg(chan, "request per-channel IRQ
  ");
d3f620b2c   Ira Snyder   fsldma: simplify ...
1047
1048
1049
  		ret = request_irq(chan->irq, fsldma_chan_irq, IRQF_SHARED,
  				  "fsldma-chan", chan);
  		if (ret) {
b158471ef   Ira Snyder   fsldma: use chann...
1050
1051
  			chan_err(chan, "unable to request per-channel IRQ
  ");
d3f620b2c   Ira Snyder   fsldma: simplify ...
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
  			goto out_unwind;
  		}
  	}
  
  	return 0;
  
  out_unwind:
  	for (/* none */; i >= 0; i--) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1063
  		if (!chan->irq)
d3f620b2c   Ira Snyder   fsldma: simplify ...
1064
1065
1066
1067
1068
1069
  			continue;
  
  		free_irq(chan->irq, chan);
  	}
  
  	return ret;
173acc7ce   Zhang Wei   dmaengine: add dr...
1070
  }
a4f56d4b1   Ira Snyder   fsldma: rename st...
1071
1072
1073
  /*----------------------------------------------------------------------------*/
  /* OpenFirmware Subsystem                                                     */
  /*----------------------------------------------------------------------------*/
463a1f8b3   Bill Pemberton   dma: remove use o...
1074
  static int fsl_dma_chan_probe(struct fsldma_device *fdev,
77cd62e80   Timur Tabi   fsldma: allow Fre...
1075
  	struct device_node *node, u32 feature, const char *compatible)
173acc7ce   Zhang Wei   dmaengine: add dr...
1076
  {
a1c033190   Ira Snyder   fsldma: rename fs...
1077
  	struct fsldma_chan *chan;
4ce0e953f   Ira Snyder   fsldma: remove un...
1078
  	struct resource res;
173acc7ce   Zhang Wei   dmaengine: add dr...
1079
  	int err;
173acc7ce   Zhang Wei   dmaengine: add dr...
1080
  	/* alloc channel */
a1c033190   Ira Snyder   fsldma: rename fs...
1081
1082
  	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
  	if (!chan) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1083
1084
1085
1086
1087
  		err = -ENOMEM;
  		goto out_return;
  	}
  
  	/* ioremap registers for use */
a1c033190   Ira Snyder   fsldma: rename fs...
1088
1089
  	chan->regs = of_iomap(node, 0);
  	if (!chan->regs) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1090
1091
1092
  		dev_err(fdev->dev, "unable to ioremap registers
  ");
  		err = -ENOMEM;
a1c033190   Ira Snyder   fsldma: rename fs...
1093
  		goto out_free_chan;
173acc7ce   Zhang Wei   dmaengine: add dr...
1094
  	}
4ce0e953f   Ira Snyder   fsldma: remove un...
1095
  	err = of_address_to_resource(node, 0, &res);
173acc7ce   Zhang Wei   dmaengine: add dr...
1096
  	if (err) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1097
1098
1099
  		dev_err(fdev->dev, "unable to find 'reg' property
  ");
  		goto out_iounmap_regs;
173acc7ce   Zhang Wei   dmaengine: add dr...
1100
  	}
a1c033190   Ira Snyder   fsldma: rename fs...
1101
  	chan->feature = feature;
173acc7ce   Zhang Wei   dmaengine: add dr...
1102
  	if (!fdev->feature)
a1c033190   Ira Snyder   fsldma: rename fs...
1103
  		fdev->feature = chan->feature;
173acc7ce   Zhang Wei   dmaengine: add dr...
1104

e7a29151d   Ira Snyder   fsldma: clean up ...
1105
1106
1107
  	/*
  	 * If the DMA device's feature is different than the feature
  	 * of its channels, report the bug
173acc7ce   Zhang Wei   dmaengine: add dr...
1108
  	 */
a1c033190   Ira Snyder   fsldma: rename fs...
1109
  	WARN_ON(fdev->feature != chan->feature);
e7a29151d   Ira Snyder   fsldma: clean up ...
1110

a1c033190   Ira Snyder   fsldma: rename fs...
1111
  	chan->dev = fdev->dev;
8de7a7d95   Hongbo Zhang   DMA: Freescale: u...
1112
1113
1114
  	chan->id = (res.start & 0xfff) < 0x300 ?
  		   ((res.start - 0x100) & 0xfff) >> 7 :
  		   ((res.start - 0x200) & 0xfff) >> 7;
a1c033190   Ira Snyder   fsldma: rename fs...
1115
  	if (chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1116
1117
  		dev_err(fdev->dev, "too many channels for device
  ");
173acc7ce   Zhang Wei   dmaengine: add dr...
1118
  		err = -EINVAL;
e7a29151d   Ira Snyder   fsldma: clean up ...
1119
  		goto out_iounmap_regs;
173acc7ce   Zhang Wei   dmaengine: add dr...
1120
  	}
173acc7ce   Zhang Wei   dmaengine: add dr...
1121

a1c033190   Ira Snyder   fsldma: rename fs...
1122
  	fdev->chan[chan->id] = chan;
59cd81876   Allen Pais   dmaengine: fsl: c...
1123
  	tasklet_setup(&chan->tasklet, dma_do_tasklet);
b158471ef   Ira Snyder   fsldma: use chann...
1124
  	snprintf(chan->name, sizeof(chan->name), "chan%d", chan->id);
e7a29151d   Ira Snyder   fsldma: clean up ...
1125
1126
  
  	/* Initialize the channel */
a1c033190   Ira Snyder   fsldma: rename fs...
1127
  	dma_init(chan);
173acc7ce   Zhang Wei   dmaengine: add dr...
1128
1129
  
  	/* Clear cdar registers */
a1c033190   Ira Snyder   fsldma: rename fs...
1130
  	set_cdar(chan, 0);
173acc7ce   Zhang Wei   dmaengine: add dr...
1131

a1c033190   Ira Snyder   fsldma: rename fs...
1132
  	switch (chan->feature & FSL_DMA_IP_MASK) {
173acc7ce   Zhang Wei   dmaengine: add dr...
1133
  	case FSL_DMA_IP_85XX:
a1c033190   Ira Snyder   fsldma: rename fs...
1134
  		chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
df561f668   Gustavo A. R. Silva   treewide: Use fal...
1135
  		fallthrough;
173acc7ce   Zhang Wei   dmaengine: add dr...
1136
  	case FSL_DMA_IP_83XX:
a1c033190   Ira Snyder   fsldma: rename fs...
1137
1138
1139
1140
  		chan->toggle_ext_start = fsl_chan_toggle_ext_start;
  		chan->set_src_loop_size = fsl_chan_set_src_loop_size;
  		chan->set_dst_loop_size = fsl_chan_set_dst_loop_size;
  		chan->set_request_count = fsl_chan_set_request_count;
173acc7ce   Zhang Wei   dmaengine: add dr...
1141
  	}
a1c033190   Ira Snyder   fsldma: rename fs...
1142
  	spin_lock_init(&chan->desc_lock);
9c3a50b7d   Ira Snyder   fsldma: major cle...
1143
1144
  	INIT_LIST_HEAD(&chan->ld_pending);
  	INIT_LIST_HEAD(&chan->ld_running);
43452fadd   Hongbo Zhang   dmaengine: Freesc...
1145
  	INIT_LIST_HEAD(&chan->ld_completed);
f04cd4070   Ira Snyder   fsldma: fix contr...
1146
  	chan->idle = true;
14c6a3333   Hongbo Zhang   dmaengine: Freesc...
1147
1148
1149
  #ifdef CONFIG_PM
  	chan->pm_state = RUNNING;
  #endif
173acc7ce   Zhang Wei   dmaengine: add dr...
1150

a1c033190   Ira Snyder   fsldma: rename fs...
1151
  	chan->common.device = &fdev->common;
8ac695463   Russell King - ARM Linux   dmaengine: ensure...
1152
  	dma_cookie_init(&chan->common);
173acc7ce   Zhang Wei   dmaengine: add dr...
1153

d3f620b2c   Ira Snyder   fsldma: simplify ...
1154
  	/* find the IRQ line, if it exists in the device tree */
a1c033190   Ira Snyder   fsldma: rename fs...
1155
  	chan->irq = irq_of_parse_and_map(node, 0);
d3f620b2c   Ira Snyder   fsldma: simplify ...
1156

173acc7ce   Zhang Wei   dmaengine: add dr...
1157
  	/* Add the channel to DMA device channel list */
a1c033190   Ira Snyder   fsldma: rename fs...
1158
  	list_add_tail(&chan->common.device_node, &fdev->common.channels);
173acc7ce   Zhang Wei   dmaengine: add dr...
1159

a1c033190   Ira Snyder   fsldma: rename fs...
1160
1161
  	dev_info(fdev->dev, "#%d (%s), irq %d
  ", chan->id, compatible,
aa570be6d   Michael Ellerman   dmaengine: NO_IRQ...
1162
  		 chan->irq ? chan->irq : fdev->irq);
173acc7ce   Zhang Wei   dmaengine: add dr...
1163
1164
  
  	return 0;
51ee87f27   Li Yang   fsldma: fix incor...
1165

e7a29151d   Ira Snyder   fsldma: clean up ...
1166
  out_iounmap_regs:
a1c033190   Ira Snyder   fsldma: rename fs...
1167
1168
1169
  	iounmap(chan->regs);
  out_free_chan:
  	kfree(chan);
e7a29151d   Ira Snyder   fsldma: clean up ...
1170
  out_return:
173acc7ce   Zhang Wei   dmaengine: add dr...
1171
1172
  	return err;
  }
a1c033190   Ira Snyder   fsldma: rename fs...
1173
  static void fsl_dma_chan_remove(struct fsldma_chan *chan)
173acc7ce   Zhang Wei   dmaengine: add dr...
1174
  {
a1c033190   Ira Snyder   fsldma: rename fs...
1175
1176
1177
1178
  	irq_dispose_mapping(chan->irq);
  	list_del(&chan->common.device_node);
  	iounmap(chan->regs);
  	kfree(chan);
173acc7ce   Zhang Wei   dmaengine: add dr...
1179
  }
463a1f8b3   Bill Pemberton   dma: remove use o...
1180
  static int fsldma_of_probe(struct platform_device *op)
173acc7ce   Zhang Wei   dmaengine: add dr...
1181
  {
a4f56d4b1   Ira Snyder   fsldma: rename st...
1182
  	struct fsldma_device *fdev;
77cd62e80   Timur Tabi   fsldma: allow Fre...
1183
  	struct device_node *child;
e7a29151d   Ira Snyder   fsldma: clean up ...
1184
  	int err;
173acc7ce   Zhang Wei   dmaengine: add dr...
1185

a4f56d4b1   Ira Snyder   fsldma: rename st...
1186
  	fdev = kzalloc(sizeof(*fdev), GFP_KERNEL);
173acc7ce   Zhang Wei   dmaengine: add dr...
1187
  	if (!fdev) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1188
1189
  		err = -ENOMEM;
  		goto out_return;
173acc7ce   Zhang Wei   dmaengine: add dr...
1190
  	}
e7a29151d   Ira Snyder   fsldma: clean up ...
1191
1192
  
  	fdev->dev = &op->dev;
173acc7ce   Zhang Wei   dmaengine: add dr...
1193
  	INIT_LIST_HEAD(&fdev->common.channels);
e7a29151d   Ira Snyder   fsldma: clean up ...
1194
  	/* ioremap the registers for use */
61c7a080a   Grant Likely   of: Always use 's...
1195
  	fdev->regs = of_iomap(op->dev.of_node, 0);
e7a29151d   Ira Snyder   fsldma: clean up ...
1196
1197
1198
1199
  	if (!fdev->regs) {
  		dev_err(&op->dev, "unable to ioremap registers
  ");
  		err = -ENOMEM;
585a1db1b   Arvind Yadav   dmaengine: fsldma...
1200
  		goto out_free;
173acc7ce   Zhang Wei   dmaengine: add dr...
1201
  	}
d3f620b2c   Ira Snyder   fsldma: simplify ...
1202
  	/* map the channel IRQ if it exists, but don't hookup the handler yet */
61c7a080a   Grant Likely   of: Always use 's...
1203
  	fdev->irq = irq_of_parse_and_map(op->dev.of_node, 0);
d3f620b2c   Ira Snyder   fsldma: simplify ...
1204

173acc7ce   Zhang Wei   dmaengine: add dr...
1205
  	dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
bbea0b6e0   Ira Snyder   fsldma: Add DMA_S...
1206
  	dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
173acc7ce   Zhang Wei   dmaengine: add dr...
1207
1208
1209
  	fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
  	fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources;
  	fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
079344818   Linus Walleij   DMAENGINE: generi...
1210
  	fdev->common.device_tx_status = fsl_tx_status;
173acc7ce   Zhang Wei   dmaengine: add dr...
1211
  	fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
b7f7552bf   Maxime Ripard   dmaengine: fsl-dm...
1212
1213
  	fdev->common.device_config = fsl_dma_device_config;
  	fdev->common.device_terminate_all = fsl_dma_device_terminate_all;
e7a29151d   Ira Snyder   fsldma: clean up ...
1214
  	fdev->common.dev = &op->dev;
173acc7ce   Zhang Wei   dmaengine: add dr...
1215

75dc1775e   Kevin Hao   dmaengine: fsldma...
1216
1217
1218
1219
  	fdev->common.src_addr_widths = FSL_DMA_BUSWIDTHS;
  	fdev->common.dst_addr_widths = FSL_DMA_BUSWIDTHS;
  	fdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
  	fdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
e2c8e425b   Li Yang   fsldma: add suppo...
1220
  	dma_set_mask(&(op->dev), DMA_BIT_MASK(36));
dd3daca16   Jingoo Han   dma: use platform...
1221
  	platform_set_drvdata(op, fdev);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1222

e7a29151d   Ira Snyder   fsldma: clean up ...
1223
1224
1225
  	/*
  	 * We cannot use of_platform_bus_probe() because there is no
  	 * of_platform_bus_remove(). Instead, we manually instantiate every DMA
77cd62e80   Timur Tabi   fsldma: allow Fre...
1226
1227
  	 * channel object.
  	 */
61c7a080a   Grant Likely   of: Always use 's...
1228
  	for_each_child_of_node(op->dev.of_node, child) {
e7a29151d   Ira Snyder   fsldma: clean up ...
1229
  		if (of_device_is_compatible(child, "fsl,eloplus-dma-channel")) {
77cd62e80   Timur Tabi   fsldma: allow Fre...
1230
1231
1232
  			fsl_dma_chan_probe(fdev, child,
  				FSL_DMA_IP_85XX | FSL_DMA_BIG_ENDIAN,
  				"fsl,eloplus-dma-channel");
e7a29151d   Ira Snyder   fsldma: clean up ...
1233
1234
1235
  		}
  
  		if (of_device_is_compatible(child, "fsl,elo-dma-channel")) {
77cd62e80   Timur Tabi   fsldma: allow Fre...
1236
1237
1238
  			fsl_dma_chan_probe(fdev, child,
  				FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN,
  				"fsl,elo-dma-channel");
e7a29151d   Ira Snyder   fsldma: clean up ...
1239
  		}
77cd62e80   Timur Tabi   fsldma: allow Fre...
1240
  	}
173acc7ce   Zhang Wei   dmaengine: add dr...
1241

d3f620b2c   Ira Snyder   fsldma: simplify ...
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
  	/*
  	 * Hookup the IRQ handler(s)
  	 *
  	 * If we have a per-controller interrupt, we prefer that to the
  	 * per-channel interrupts to reduce the number of shared interrupt
  	 * handlers on the same IRQ line
  	 */
  	err = fsldma_request_irqs(fdev);
  	if (err) {
  		dev_err(fdev->dev, "unable to request IRQs
  ");
  		goto out_free_fdev;
  	}
173acc7ce   Zhang Wei   dmaengine: add dr...
1255
1256
  	dma_async_device_register(&fdev->common);
  	return 0;
e7a29151d   Ira Snyder   fsldma: clean up ...
1257
  out_free_fdev:
d3f620b2c   Ira Snyder   fsldma: simplify ...
1258
  	irq_dispose_mapping(fdev->irq);
585a1db1b   Arvind Yadav   dmaengine: fsldma...
1259
1260
  	iounmap(fdev->regs);
  out_free:
173acc7ce   Zhang Wei   dmaengine: add dr...
1261
  	kfree(fdev);
e7a29151d   Ira Snyder   fsldma: clean up ...
1262
  out_return:
173acc7ce   Zhang Wei   dmaengine: add dr...
1263
1264
  	return err;
  }
2dc115813   Grant Likely   of/device: Replac...
1265
  static int fsldma_of_remove(struct platform_device *op)
77cd62e80   Timur Tabi   fsldma: allow Fre...
1266
  {
a4f56d4b1   Ira Snyder   fsldma: rename st...
1267
  	struct fsldma_device *fdev;
77cd62e80   Timur Tabi   fsldma: allow Fre...
1268
  	unsigned int i;
dd3daca16   Jingoo Han   dma: use platform...
1269
  	fdev = platform_get_drvdata(op);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1270
  	dma_async_device_unregister(&fdev->common);
d3f620b2c   Ira Snyder   fsldma: simplify ...
1271
  	fsldma_free_irqs(fdev);
e7a29151d   Ira Snyder   fsldma: clean up ...
1272
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
77cd62e80   Timur Tabi   fsldma: allow Fre...
1273
1274
  		if (fdev->chan[i])
  			fsl_dma_chan_remove(fdev->chan[i]);
e7a29151d   Ira Snyder   fsldma: clean up ...
1275
  	}
77cd62e80   Timur Tabi   fsldma: allow Fre...
1276

e7a29151d   Ira Snyder   fsldma: clean up ...
1277
  	iounmap(fdev->regs);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1278
  	kfree(fdev);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1279
1280
1281
  
  	return 0;
  }
14c6a3333   Hongbo Zhang   dmaengine: Freesc...
1282
1283
1284
  #ifdef CONFIG_PM
  static int fsldma_suspend_late(struct device *dev)
  {
03bf2793f   Wolfram Sang   dmaengine: fsldma...
1285
  	struct fsldma_device *fdev = dev_get_drvdata(dev);
14c6a3333   Hongbo Zhang   dmaengine: Freesc...
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
  	struct fsldma_chan *chan;
  	int i;
  
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
  
  		spin_lock_bh(&chan->desc_lock);
  		if (unlikely(!chan->idle))
  			goto out;
  		chan->regs_save.mr = get_mr(chan);
  		chan->pm_state = SUSPENDED;
  		spin_unlock_bh(&chan->desc_lock);
  	}
  	return 0;
  
  out:
  	for (; i >= 0; i--) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
  		chan->pm_state = RUNNING;
  		spin_unlock_bh(&chan->desc_lock);
  	}
  	return -EBUSY;
  }
  
  static int fsldma_resume_early(struct device *dev)
  {
03bf2793f   Wolfram Sang   dmaengine: fsldma...
1316
  	struct fsldma_device *fdev = dev_get_drvdata(dev);
14c6a3333   Hongbo Zhang   dmaengine: Freesc...
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
  	struct fsldma_chan *chan;
  	u32 mode;
  	int i;
  
  	for (i = 0; i < FSL_DMA_MAX_CHANS_PER_DEVICE; i++) {
  		chan = fdev->chan[i];
  		if (!chan)
  			continue;
  
  		spin_lock_bh(&chan->desc_lock);
  		mode = chan->regs_save.mr
  			& ~FSL_DMA_MR_CS & ~FSL_DMA_MR_CC & ~FSL_DMA_MR_CA;
  		set_mr(chan, mode);
  		chan->pm_state = RUNNING;
  		spin_unlock_bh(&chan->desc_lock);
  	}
  
  	return 0;
  }
  
  static const struct dev_pm_ops fsldma_pm_ops = {
  	.suspend_late	= fsldma_suspend_late,
  	.resume_early	= fsldma_resume_early,
  };
  #endif
4b1cf1fac   Márton Németh   dma: make Open Fi...
1342
  static const struct of_device_id fsldma_of_ids[] = {
8de7a7d95   Hongbo Zhang   DMA: Freescale: u...
1343
  	{ .compatible = "fsl,elo3-dma", },
049c9d455   Kumar Gala   [POWERPC] fsldma:...
1344
1345
  	{ .compatible = "fsl,eloplus-dma", },
  	{ .compatible = "fsl,elo-dma", },
173acc7ce   Zhang Wei   dmaengine: add dr...
1346
1347
  	{}
  };
7522c2402   Luis de Bethencourt   dmaengine: fsldma...
1348
  MODULE_DEVICE_TABLE(of, fsldma_of_ids);
173acc7ce   Zhang Wei   dmaengine: add dr...
1349

8faa7cf82   Ira W. Snyder   dt/fsldma: fix bu...
1350
  static struct platform_driver fsldma_of_driver = {
4018294b5   Grant Likely   of: Remove duplic...
1351
1352
  	.driver = {
  		.name = "fsl-elo-dma",
4018294b5   Grant Likely   of: Remove duplic...
1353
  		.of_match_table = fsldma_of_ids,
14c6a3333   Hongbo Zhang   dmaengine: Freesc...
1354
1355
1356
  #ifdef CONFIG_PM
  		.pm = &fsldma_pm_ops,
  #endif
4018294b5   Grant Likely   of: Remove duplic...
1357
1358
1359
  	},
  	.probe = fsldma_of_probe,
  	.remove = fsldma_of_remove,
173acc7ce   Zhang Wei   dmaengine: add dr...
1360
  };
a4f56d4b1   Ira Snyder   fsldma: rename st...
1361
1362
1363
1364
1365
  /*----------------------------------------------------------------------------*/
  /* Module Init / Exit                                                         */
  /*----------------------------------------------------------------------------*/
  
  static __init int fsldma_init(void)
173acc7ce   Zhang Wei   dmaengine: add dr...
1366
  {
8de7a7d95   Hongbo Zhang   DMA: Freescale: u...
1367
1368
  	pr_info("Freescale Elo series DMA driver
  ");
000061245   Grant Likely   dt/powerpc: Elimi...
1369
  	return platform_driver_register(&fsldma_of_driver);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1370
  }
a4f56d4b1   Ira Snyder   fsldma: rename st...
1371
  static void __exit fsldma_exit(void)
77cd62e80   Timur Tabi   fsldma: allow Fre...
1372
  {
000061245   Grant Likely   dt/powerpc: Elimi...
1373
  	platform_driver_unregister(&fsldma_of_driver);
173acc7ce   Zhang Wei   dmaengine: add dr...
1374
  }
a4f56d4b1   Ira Snyder   fsldma: rename st...
1375
1376
  subsys_initcall(fsldma_init);
  module_exit(fsldma_exit);
77cd62e80   Timur Tabi   fsldma: allow Fre...
1377

8de7a7d95   Hongbo Zhang   DMA: Freescale: u...
1378
  MODULE_DESCRIPTION("Freescale Elo series DMA driver");
77cd62e80   Timur Tabi   fsldma: allow Fre...
1379
  MODULE_LICENSE("GPL");