Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d6be34fb JL |
2 | /* |
3 | * drivers/dma/fsl-edma.c | |
4 | * | |
5 | * Copyright 2013-2014 Freescale Semiconductor, Inc. | |
6 | * | |
7 | * Driver for the Freescale eDMA engine with flexible channel multiplexing | |
8 | * capability for DMA request sources. The eDMA block can be found on some | |
9 | * Vybrid and Layerscape SoCs. | |
d6be34fb JL |
10 | */ |
11 | ||
d6be34fb JL |
12 | #include <linux/module.h> |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/clk.h> | |
d6be34fb JL |
15 | #include <linux/of.h> |
16 | #include <linux/of_device.h> | |
17 | #include <linux/of_address.h> | |
18 | #include <linux/of_irq.h> | |
19 | #include <linux/of_dma.h> | |
20 | ||
9d831528 | 21 | #include "fsl-edma-common.h" |
d6be34fb | 22 | |
ba1cab79 AS |
23 | static void fsl_edma_synchronize(struct dma_chan *chan) |
24 | { | |
25 | struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); | |
26 | ||
27 | vchan_synchronize(&fsl_chan->vchan); | |
28 | } | |
29 | ||
d6be34fb JL |
30 | static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) |
31 | { | |
32 | struct fsl_edma_engine *fsl_edma = dev_id; | |
33 | unsigned int intr, ch; | |
377eaf3b | 34 | struct edma_regs *regs = &fsl_edma->regs; |
d6be34fb JL |
35 | struct fsl_edma_chan *fsl_chan; |
36 | ||
377eaf3b | 37 | intr = edma_readl(fsl_edma, regs->intl); |
d6be34fb JL |
38 | if (!intr) |
39 | return IRQ_NONE; | |
40 | ||
41 | for (ch = 0; ch < fsl_edma->n_chans; ch++) { | |
42 | if (intr & (0x1 << ch)) { | |
377eaf3b | 43 | edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint); |
d6be34fb JL |
44 | |
45 | fsl_chan = &fsl_edma->chans[ch]; | |
46 | ||
47 | spin_lock(&fsl_chan->vchan.lock); | |
48 | if (!fsl_chan->edesc->iscyclic) { | |
49 | list_del(&fsl_chan->edesc->vdesc.node); | |
50 | vchan_cookie_complete(&fsl_chan->edesc->vdesc); | |
51 | fsl_chan->edesc = NULL; | |
52 | fsl_chan->status = DMA_COMPLETE; | |
82d149b8 | 53 | fsl_chan->idle = true; |
d6be34fb JL |
54 | } else { |
55 | vchan_cyclic_callback(&fsl_chan->edesc->vdesc); | |
56 | } | |
57 | ||
58 | if (!fsl_chan->edesc) | |
59 | fsl_edma_xfer_desc(fsl_chan); | |
60 | ||
61 | spin_unlock(&fsl_chan->vchan.lock); | |
62 | } | |
63 | } | |
64 | return IRQ_HANDLED; | |
65 | } | |
66 | ||
67 | static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) | |
68 | { | |
69 | struct fsl_edma_engine *fsl_edma = dev_id; | |
70 | unsigned int err, ch; | |
377eaf3b | 71 | struct edma_regs *regs = &fsl_edma->regs; |
d6be34fb | 72 | |
377eaf3b | 73 | err = edma_readl(fsl_edma, regs->errl); |
d6be34fb JL |
74 | if (!err) |
75 | return IRQ_NONE; | |
76 | ||
77 | for (ch = 0; ch < fsl_edma->n_chans; ch++) { | |
78 | if (err & (0x1 << ch)) { | |
79 | fsl_edma_disable_request(&fsl_edma->chans[ch]); | |
377eaf3b | 80 | edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr); |
d6be34fb | 81 | fsl_edma->chans[ch].status = DMA_ERROR; |
82d149b8 | 82 | fsl_edma->chans[ch].idle = true; |
d6be34fb JL |
83 | } |
84 | } | |
85 | return IRQ_HANDLED; | |
86 | } | |
87 | ||
88 | static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id) | |
89 | { | |
90 | if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED) | |
91 | return IRQ_HANDLED; | |
92 | ||
93 | return fsl_edma_err_handler(irq, dev_id); | |
94 | } | |
95 | ||
d6be34fb JL |
96 | static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, |
97 | struct of_dma *ofdma) | |
98 | { | |
99 | struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; | |
178c81e5 | 100 | struct dma_chan *chan, *_chan; |
82d149b8 | 101 | struct fsl_edma_chan *fsl_chan; |
af802728 RG |
102 | u32 dmamux_nr = fsl_edma->drvdata->dmamuxs; |
103 | unsigned long chans_per_mux = fsl_edma->n_chans / dmamux_nr; | |
d6be34fb JL |
104 | |
105 | if (dma_spec->args_count != 2) | |
106 | return NULL; | |
107 | ||
108 | mutex_lock(&fsl_edma->fsl_edma_mutex); | |
178c81e5 | 109 | list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { |
d6be34fb JL |
110 | if (chan->client_count) |
111 | continue; | |
211bfef7 | 112 | if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) { |
d6be34fb JL |
113 | chan = dma_get_slave_channel(chan); |
114 | if (chan) { | |
115 | chan->device->privatecnt++; | |
82d149b8 YY |
116 | fsl_chan = to_fsl_edma_chan(chan); |
117 | fsl_chan->slave_id = dma_spec->args[1]; | |
118 | fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, | |
119 | true); | |
d6be34fb JL |
120 | mutex_unlock(&fsl_edma->fsl_edma_mutex); |
121 | return chan; | |
122 | } | |
123 | } | |
124 | } | |
125 | mutex_unlock(&fsl_edma->fsl_edma_mutex); | |
126 | return NULL; | |
127 | } | |
128 | ||
d6be34fb JL |
129 | static int |
130 | fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) | |
131 | { | |
132 | int ret; | |
133 | ||
134 | fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx"); | |
e17be6e1 | 135 | if (fsl_edma->txirq < 0) |
d6be34fb | 136 | return fsl_edma->txirq; |
d6be34fb JL |
137 | |
138 | fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err"); | |
e17be6e1 | 139 | if (fsl_edma->errirq < 0) |
d6be34fb | 140 | return fsl_edma->errirq; |
d6be34fb JL |
141 | |
142 | if (fsl_edma->txirq == fsl_edma->errirq) { | |
143 | ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, | |
144 | fsl_edma_irq_handler, 0, "eDMA", fsl_edma); | |
145 | if (ret) { | |
146 | dev_err(&pdev->dev, "Can't register eDMA IRQ.\n"); | |
e095189a | 147 | return ret; |
d6be34fb JL |
148 | } |
149 | } else { | |
150 | ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, | |
151 | fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma); | |
152 | if (ret) { | |
153 | dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n"); | |
e095189a | 154 | return ret; |
d6be34fb JL |
155 | } |
156 | ||
157 | ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, | |
158 | fsl_edma_err_handler, 0, "eDMA err", fsl_edma); | |
159 | if (ret) { | |
160 | dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n"); | |
e095189a | 161 | return ret; |
d6be34fb JL |
162 | } |
163 | } | |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
232a7f18 RG |
168 | static int |
169 | fsl_edma2_irq_init(struct platform_device *pdev, | |
170 | struct fsl_edma_engine *fsl_edma) | |
171 | { | |
172 | int i, ret, irq; | |
173 | int count; | |
174 | ||
175 | count = platform_irq_count(pdev); | |
176 | dev_dbg(&pdev->dev, "%s Found %d interrupts\r\n", __func__, count); | |
177 | if (count <= 2) { | |
178 | dev_err(&pdev->dev, "Interrupts in DTS not correct.\n"); | |
179 | return -EINVAL; | |
180 | } | |
181 | /* | |
182 | * 16 channel independent interrupts + 1 error interrupt on i.mx7ulp. | |
183 | * 2 channel share one interrupt, for example, ch0/ch16, ch1/ch17... | |
184 | * For now, just simply request irq without IRQF_SHARED flag, since 16 | |
185 | * channels are enough on i.mx7ulp whose M4 domain own some peripherals. | |
186 | */ | |
187 | for (i = 0; i < count; i++) { | |
188 | irq = platform_get_irq(pdev, i); | |
189 | if (irq < 0) | |
190 | return -ENXIO; | |
191 | ||
192 | sprintf(fsl_edma->chans[i].chan_name, "eDMA2-CH%02d", i); | |
193 | ||
194 | /* The last IRQ is for eDMA err */ | |
195 | if (i == count - 1) | |
196 | ret = devm_request_irq(&pdev->dev, irq, | |
197 | fsl_edma_err_handler, | |
198 | 0, "eDMA2-ERR", fsl_edma); | |
199 | else | |
200 | ret = devm_request_irq(&pdev->dev, irq, | |
201 | fsl_edma_tx_handler, 0, | |
202 | fsl_edma->chans[i].chan_name, | |
203 | fsl_edma); | |
204 | if (ret) | |
205 | return ret; | |
206 | } | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
476c7c80 VK |
211 | static void fsl_edma_irq_exit( |
212 | struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) | |
213 | { | |
214 | if (fsl_edma->txirq == fsl_edma->errirq) { | |
215 | devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma); | |
216 | } else { | |
217 | devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma); | |
218 | devm_free_irq(&pdev->dev, fsl_edma->errirq, fsl_edma); | |
219 | } | |
220 | } | |
221 | ||
2610acf4 | 222 | static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks) |
5e2fe1e7 PG |
223 | { |
224 | int i; | |
225 | ||
2610acf4 | 226 | for (i = 0; i < nr_clocks; i++) |
5e2fe1e7 PG |
227 | clk_disable_unprepare(fsl_edma->muxclk[i]); |
228 | } | |
229 | ||
af802728 RG |
230 | static struct fsl_edma_drvdata vf610_data = { |
231 | .version = v1, | |
232 | .dmamuxs = DMAMUX_NR, | |
233 | .setup_irq = fsl_edma_irq_init, | |
234 | }; | |
235 | ||
232a7f18 RG |
236 | static struct fsl_edma_drvdata imx7ulp_data = { |
237 | .version = v3, | |
238 | .dmamuxs = 1, | |
239 | .has_dmaclk = true, | |
240 | .setup_irq = fsl_edma2_irq_init, | |
241 | }; | |
242 | ||
af802728 RG |
243 | static const struct of_device_id fsl_edma_dt_ids[] = { |
244 | { .compatible = "fsl,vf610-edma", .data = &vf610_data}, | |
232a7f18 | 245 | { .compatible = "fsl,imx7ulp-edma", .data = &imx7ulp_data}, |
af802728 RG |
246 | { /* sentinel */ } |
247 | }; | |
248 | MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); | |
249 | ||
d6be34fb JL |
250 | static int fsl_edma_probe(struct platform_device *pdev) |
251 | { | |
af802728 RG |
252 | const struct of_device_id *of_id = |
253 | of_match_device(fsl_edma_dt_ids, &pdev->dev); | |
d6be34fb JL |
254 | struct device_node *np = pdev->dev.of_node; |
255 | struct fsl_edma_engine *fsl_edma; | |
af802728 | 256 | const struct fsl_edma_drvdata *drvdata = NULL; |
d6be34fb | 257 | struct fsl_edma_chan *fsl_chan; |
377eaf3b | 258 | struct edma_regs *regs; |
d6be34fb JL |
259 | struct resource *res; |
260 | int len, chans; | |
261 | int ret, i; | |
262 | ||
af802728 RG |
263 | if (of_id) |
264 | drvdata = of_id->data; | |
265 | if (!drvdata) { | |
266 | dev_err(&pdev->dev, "unable to find driver data\n"); | |
267 | return -EINVAL; | |
268 | } | |
269 | ||
d6be34fb JL |
270 | ret = of_property_read_u32(np, "dma-channels", &chans); |
271 | if (ret) { | |
272 | dev_err(&pdev->dev, "Can't get dma-channels.\n"); | |
273 | return ret; | |
274 | } | |
275 | ||
276 | len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans; | |
277 | fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); | |
278 | if (!fsl_edma) | |
279 | return -ENOMEM; | |
280 | ||
af802728 | 281 | fsl_edma->drvdata = drvdata; |
d6be34fb JL |
282 | fsl_edma->n_chans = chans; |
283 | mutex_init(&fsl_edma->fsl_edma_mutex); | |
284 | ||
285 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
286 | fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res); | |
287 | if (IS_ERR(fsl_edma->membase)) | |
288 | return PTR_ERR(fsl_edma->membase); | |
289 | ||
377eaf3b AD |
290 | fsl_edma_setup_regs(fsl_edma); |
291 | regs = &fsl_edma->regs; | |
292 | ||
232a7f18 RG |
293 | if (drvdata->has_dmaclk) { |
294 | fsl_edma->dmaclk = devm_clk_get(&pdev->dev, "dma"); | |
295 | if (IS_ERR(fsl_edma->dmaclk)) { | |
296 | dev_err(&pdev->dev, "Missing DMA block clock.\n"); | |
297 | return PTR_ERR(fsl_edma->dmaclk); | |
298 | } | |
299 | ||
300 | ret = clk_prepare_enable(fsl_edma->dmaclk); | |
301 | if (ret) { | |
302 | dev_err(&pdev->dev, "DMA clk block failed.\n"); | |
303 | return ret; | |
304 | } | |
305 | } | |
306 | ||
af802728 | 307 | for (i = 0; i < fsl_edma->drvdata->dmamuxs; i++) { |
d6be34fb JL |
308 | char clkname[32]; |
309 | ||
310 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); | |
311 | fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res); | |
2610acf4 AP |
312 | if (IS_ERR(fsl_edma->muxbase[i])) { |
313 | /* on error: disable all previously enabled clks */ | |
314 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb | 315 | return PTR_ERR(fsl_edma->muxbase[i]); |
2610acf4 | 316 | } |
d6be34fb JL |
317 | |
318 | sprintf(clkname, "dmamux%d", i); | |
319 | fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname); | |
320 | if (IS_ERR(fsl_edma->muxclk[i])) { | |
321 | dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); | |
2610acf4 AP |
322 | /* on error: disable all previously enabled clks */ |
323 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb JL |
324 | return PTR_ERR(fsl_edma->muxclk[i]); |
325 | } | |
326 | ||
327 | ret = clk_prepare_enable(fsl_edma->muxclk[i]); | |
2610acf4 AP |
328 | if (ret) |
329 | /* on error: disable all previously enabled clks */ | |
330 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb JL |
331 | |
332 | } | |
333 | ||
d6be34fb JL |
334 | fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); |
335 | ||
336 | INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); | |
337 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
338 | struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; | |
339 | ||
340 | fsl_chan->edma = fsl_edma; | |
82d149b8 YY |
341 | fsl_chan->pm_state = RUNNING; |
342 | fsl_chan->slave_id = 0; | |
343 | fsl_chan->idle = true; | |
0fa89f97 | 344 | fsl_chan->dma_dir = DMA_NONE; |
d6be34fb JL |
345 | fsl_chan->vchan.desc_free = fsl_edma_free_desc; |
346 | vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); | |
347 | ||
377eaf3b | 348 | edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); |
d6be34fb JL |
349 | fsl_edma_chan_mux(fsl_chan, 0, false); |
350 | } | |
351 | ||
377eaf3b | 352 | edma_writel(fsl_edma, ~0, regs->intl); |
af802728 | 353 | ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma); |
0fe25d61 SA |
354 | if (ret) |
355 | return ret; | |
356 | ||
d6be34fb JL |
357 | dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); |
358 | dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); | |
359 | dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask); | |
360 | ||
361 | fsl_edma->dma_dev.dev = &pdev->dev; | |
362 | fsl_edma->dma_dev.device_alloc_chan_resources | |
363 | = fsl_edma_alloc_chan_resources; | |
364 | fsl_edma->dma_dev.device_free_chan_resources | |
365 | = fsl_edma_free_chan_resources; | |
366 | fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status; | |
367 | fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; | |
368 | fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic; | |
d80f381f MR |
369 | fsl_edma->dma_dev.device_config = fsl_edma_slave_config; |
370 | fsl_edma->dma_dev.device_pause = fsl_edma_pause; | |
371 | fsl_edma->dma_dev.device_resume = fsl_edma_resume; | |
372 | fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; | |
ba1cab79 | 373 | fsl_edma->dma_dev.device_synchronize = fsl_edma_synchronize; |
d6be34fb | 374 | fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; |
f45c4311 MR |
375 | |
376 | fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; | |
377 | fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; | |
378 | fsl_edma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); | |
d6be34fb JL |
379 | |
380 | platform_set_drvdata(pdev, fsl_edma); | |
381 | ||
382 | ret = dma_async_device_register(&fsl_edma->dma_dev); | |
383 | if (ret) { | |
a86144da PG |
384 | dev_err(&pdev->dev, |
385 | "Can't register Freescale eDMA engine. (%d)\n", ret); | |
af802728 | 386 | fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs); |
d6be34fb JL |
387 | return ret; |
388 | } | |
389 | ||
390 | ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma); | |
391 | if (ret) { | |
a86144da PG |
392 | dev_err(&pdev->dev, |
393 | "Can't register Freescale eDMA of_dma. (%d)\n", ret); | |
d6be34fb | 394 | dma_async_device_unregister(&fsl_edma->dma_dev); |
af802728 | 395 | fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs); |
d6be34fb JL |
396 | return ret; |
397 | } | |
398 | ||
399 | /* enable round robin arbitration */ | |
377eaf3b | 400 | edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); |
d6be34fb JL |
401 | |
402 | return 0; | |
403 | } | |
404 | ||
405 | static int fsl_edma_remove(struct platform_device *pdev) | |
406 | { | |
407 | struct device_node *np = pdev->dev.of_node; | |
408 | struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev); | |
d6be34fb | 409 | |
476c7c80 | 410 | fsl_edma_irq_exit(pdev, fsl_edma); |
6f93b93b | 411 | fsl_edma_cleanup_vchan(&fsl_edma->dma_dev); |
d6be34fb JL |
412 | of_dma_controller_free(np); |
413 | dma_async_device_unregister(&fsl_edma->dma_dev); | |
af802728 | 414 | fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs); |
d6be34fb JL |
415 | |
416 | return 0; | |
417 | } | |
418 | ||
82d149b8 YY |
419 | static int fsl_edma_suspend_late(struct device *dev) |
420 | { | |
421 | struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); | |
422 | struct fsl_edma_chan *fsl_chan; | |
423 | unsigned long flags; | |
424 | int i; | |
425 | ||
426 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
427 | fsl_chan = &fsl_edma->chans[i]; | |
428 | spin_lock_irqsave(&fsl_chan->vchan.lock, flags); | |
429 | /* Make sure chan is idle or will force disable. */ | |
430 | if (unlikely(!fsl_chan->idle)) { | |
431 | dev_warn(dev, "WARN: There is non-idle channel."); | |
432 | fsl_edma_disable_request(fsl_chan); | |
433 | fsl_edma_chan_mux(fsl_chan, 0, false); | |
434 | } | |
435 | ||
436 | fsl_chan->pm_state = SUSPENDED; | |
437 | spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); | |
438 | } | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
443 | static int fsl_edma_resume_early(struct device *dev) | |
444 | { | |
445 | struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); | |
446 | struct fsl_edma_chan *fsl_chan; | |
377eaf3b | 447 | struct edma_regs *regs = &fsl_edma->regs; |
82d149b8 YY |
448 | int i; |
449 | ||
450 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
451 | fsl_chan = &fsl_edma->chans[i]; | |
452 | fsl_chan->pm_state = RUNNING; | |
377eaf3b | 453 | edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); |
82d149b8 YY |
454 | if (fsl_chan->slave_id != 0) |
455 | fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); | |
456 | } | |
457 | ||
377eaf3b | 458 | edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); |
82d149b8 YY |
459 | |
460 | return 0; | |
461 | } | |
462 | ||
463 | /* | |
464 | * eDMA provides the service to others, so it should be suspend late | |
465 | * and resume early. When eDMA suspend, all of the clients should stop | |
466 | * the DMA data transmission and let the channel idle. | |
467 | */ | |
468 | static const struct dev_pm_ops fsl_edma_pm_ops = { | |
469 | .suspend_late = fsl_edma_suspend_late, | |
470 | .resume_early = fsl_edma_resume_early, | |
471 | }; | |
472 | ||
d6be34fb JL |
473 | static struct platform_driver fsl_edma_driver = { |
474 | .driver = { | |
475 | .name = "fsl-edma", | |
d6be34fb | 476 | .of_match_table = fsl_edma_dt_ids, |
82d149b8 | 477 | .pm = &fsl_edma_pm_ops, |
d6be34fb JL |
478 | }, |
479 | .probe = fsl_edma_probe, | |
480 | .remove = fsl_edma_remove, | |
481 | }; | |
482 | ||
8edc51c1 YY |
483 | static int __init fsl_edma_init(void) |
484 | { | |
485 | return platform_driver_register(&fsl_edma_driver); | |
486 | } | |
487 | subsys_initcall(fsl_edma_init); | |
488 | ||
489 | static void __exit fsl_edma_exit(void) | |
490 | { | |
491 | platform_driver_unregister(&fsl_edma_driver); | |
492 | } | |
493 | module_exit(fsl_edma_exit); | |
d6be34fb JL |
494 | |
495 | MODULE_ALIAS("platform:fsl-edma"); | |
496 | MODULE_DESCRIPTION("Freescale eDMA engine driver"); | |
497 | MODULE_LICENSE("GPL v2"); |