]>
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 JL |
22 | |
23 | static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) | |
24 | { | |
25 | struct fsl_edma_engine *fsl_edma = dev_id; | |
26 | unsigned int intr, ch; | |
377eaf3b | 27 | struct edma_regs *regs = &fsl_edma->regs; |
d6be34fb JL |
28 | struct fsl_edma_chan *fsl_chan; |
29 | ||
377eaf3b | 30 | intr = edma_readl(fsl_edma, regs->intl); |
d6be34fb JL |
31 | if (!intr) |
32 | return IRQ_NONE; | |
33 | ||
34 | for (ch = 0; ch < fsl_edma->n_chans; ch++) { | |
35 | if (intr & (0x1 << ch)) { | |
377eaf3b | 36 | edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint); |
d6be34fb JL |
37 | |
38 | fsl_chan = &fsl_edma->chans[ch]; | |
39 | ||
40 | spin_lock(&fsl_chan->vchan.lock); | |
41 | if (!fsl_chan->edesc->iscyclic) { | |
42 | list_del(&fsl_chan->edesc->vdesc.node); | |
43 | vchan_cookie_complete(&fsl_chan->edesc->vdesc); | |
44 | fsl_chan->edesc = NULL; | |
45 | fsl_chan->status = DMA_COMPLETE; | |
82d149b8 | 46 | fsl_chan->idle = true; |
d6be34fb JL |
47 | } else { |
48 | vchan_cyclic_callback(&fsl_chan->edesc->vdesc); | |
49 | } | |
50 | ||
51 | if (!fsl_chan->edesc) | |
52 | fsl_edma_xfer_desc(fsl_chan); | |
53 | ||
54 | spin_unlock(&fsl_chan->vchan.lock); | |
55 | } | |
56 | } | |
57 | return IRQ_HANDLED; | |
58 | } | |
59 | ||
60 | static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) | |
61 | { | |
62 | struct fsl_edma_engine *fsl_edma = dev_id; | |
63 | unsigned int err, ch; | |
377eaf3b | 64 | struct edma_regs *regs = &fsl_edma->regs; |
d6be34fb | 65 | |
377eaf3b | 66 | err = edma_readl(fsl_edma, regs->errl); |
d6be34fb JL |
67 | if (!err) |
68 | return IRQ_NONE; | |
69 | ||
70 | for (ch = 0; ch < fsl_edma->n_chans; ch++) { | |
71 | if (err & (0x1 << ch)) { | |
72 | fsl_edma_disable_request(&fsl_edma->chans[ch]); | |
377eaf3b | 73 | edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr); |
d6be34fb | 74 | fsl_edma->chans[ch].status = DMA_ERROR; |
82d149b8 | 75 | fsl_edma->chans[ch].idle = true; |
d6be34fb JL |
76 | } |
77 | } | |
78 | return IRQ_HANDLED; | |
79 | } | |
80 | ||
81 | static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id) | |
82 | { | |
83 | if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED) | |
84 | return IRQ_HANDLED; | |
85 | ||
86 | return fsl_edma_err_handler(irq, dev_id); | |
87 | } | |
88 | ||
d6be34fb JL |
89 | static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, |
90 | struct of_dma *ofdma) | |
91 | { | |
92 | struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; | |
178c81e5 | 93 | struct dma_chan *chan, *_chan; |
82d149b8 | 94 | struct fsl_edma_chan *fsl_chan; |
211bfef7 | 95 | unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR; |
d6be34fb JL |
96 | |
97 | if (dma_spec->args_count != 2) | |
98 | return NULL; | |
99 | ||
100 | mutex_lock(&fsl_edma->fsl_edma_mutex); | |
178c81e5 | 101 | list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { |
d6be34fb JL |
102 | if (chan->client_count) |
103 | continue; | |
211bfef7 | 104 | if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) { |
d6be34fb JL |
105 | chan = dma_get_slave_channel(chan); |
106 | if (chan) { | |
107 | chan->device->privatecnt++; | |
82d149b8 YY |
108 | fsl_chan = to_fsl_edma_chan(chan); |
109 | fsl_chan->slave_id = dma_spec->args[1]; | |
110 | fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, | |
111 | true); | |
d6be34fb JL |
112 | mutex_unlock(&fsl_edma->fsl_edma_mutex); |
113 | return chan; | |
114 | } | |
115 | } | |
116 | } | |
117 | mutex_unlock(&fsl_edma->fsl_edma_mutex); | |
118 | return NULL; | |
119 | } | |
120 | ||
d6be34fb JL |
121 | static int |
122 | fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) | |
123 | { | |
124 | int ret; | |
125 | ||
126 | fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx"); | |
127 | if (fsl_edma->txirq < 0) { | |
128 | dev_err(&pdev->dev, "Can't get edma-tx irq.\n"); | |
129 | return fsl_edma->txirq; | |
130 | } | |
131 | ||
132 | fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err"); | |
133 | if (fsl_edma->errirq < 0) { | |
134 | dev_err(&pdev->dev, "Can't get edma-err irq.\n"); | |
135 | return fsl_edma->errirq; | |
136 | } | |
137 | ||
138 | if (fsl_edma->txirq == fsl_edma->errirq) { | |
139 | ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, | |
140 | fsl_edma_irq_handler, 0, "eDMA", fsl_edma); | |
141 | if (ret) { | |
142 | dev_err(&pdev->dev, "Can't register eDMA IRQ.\n"); | |
e095189a | 143 | return ret; |
d6be34fb JL |
144 | } |
145 | } else { | |
146 | ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, | |
147 | fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma); | |
148 | if (ret) { | |
149 | dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n"); | |
e095189a | 150 | return ret; |
d6be34fb JL |
151 | } |
152 | ||
153 | ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, | |
154 | fsl_edma_err_handler, 0, "eDMA err", fsl_edma); | |
155 | if (ret) { | |
156 | dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n"); | |
e095189a | 157 | return ret; |
d6be34fb JL |
158 | } |
159 | } | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
476c7c80 VK |
164 | static void fsl_edma_irq_exit( |
165 | struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) | |
166 | { | |
167 | if (fsl_edma->txirq == fsl_edma->errirq) { | |
168 | devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma); | |
169 | } else { | |
170 | devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma); | |
171 | devm_free_irq(&pdev->dev, fsl_edma->errirq, fsl_edma); | |
172 | } | |
173 | } | |
174 | ||
2610acf4 | 175 | static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks) |
5e2fe1e7 PG |
176 | { |
177 | int i; | |
178 | ||
2610acf4 | 179 | for (i = 0; i < nr_clocks; i++) |
5e2fe1e7 PG |
180 | clk_disable_unprepare(fsl_edma->muxclk[i]); |
181 | } | |
182 | ||
d6be34fb JL |
183 | static int fsl_edma_probe(struct platform_device *pdev) |
184 | { | |
185 | struct device_node *np = pdev->dev.of_node; | |
186 | struct fsl_edma_engine *fsl_edma; | |
187 | struct fsl_edma_chan *fsl_chan; | |
377eaf3b | 188 | struct edma_regs *regs; |
d6be34fb JL |
189 | struct resource *res; |
190 | int len, chans; | |
191 | int ret, i; | |
192 | ||
193 | ret = of_property_read_u32(np, "dma-channels", &chans); | |
194 | if (ret) { | |
195 | dev_err(&pdev->dev, "Can't get dma-channels.\n"); | |
196 | return ret; | |
197 | } | |
198 | ||
199 | len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans; | |
200 | fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); | |
201 | if (!fsl_edma) | |
202 | return -ENOMEM; | |
203 | ||
377eaf3b | 204 | fsl_edma->version = v1; |
d6be34fb JL |
205 | fsl_edma->n_chans = chans; |
206 | mutex_init(&fsl_edma->fsl_edma_mutex); | |
207 | ||
208 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
209 | fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res); | |
210 | if (IS_ERR(fsl_edma->membase)) | |
211 | return PTR_ERR(fsl_edma->membase); | |
212 | ||
377eaf3b AD |
213 | fsl_edma_setup_regs(fsl_edma); |
214 | regs = &fsl_edma->regs; | |
215 | ||
d6be34fb JL |
216 | for (i = 0; i < DMAMUX_NR; i++) { |
217 | char clkname[32]; | |
218 | ||
219 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); | |
220 | fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res); | |
2610acf4 AP |
221 | if (IS_ERR(fsl_edma->muxbase[i])) { |
222 | /* on error: disable all previously enabled clks */ | |
223 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb | 224 | return PTR_ERR(fsl_edma->muxbase[i]); |
2610acf4 | 225 | } |
d6be34fb JL |
226 | |
227 | sprintf(clkname, "dmamux%d", i); | |
228 | fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname); | |
229 | if (IS_ERR(fsl_edma->muxclk[i])) { | |
230 | dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); | |
2610acf4 AP |
231 | /* on error: disable all previously enabled clks */ |
232 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb JL |
233 | return PTR_ERR(fsl_edma->muxclk[i]); |
234 | } | |
235 | ||
236 | ret = clk_prepare_enable(fsl_edma->muxclk[i]); | |
2610acf4 AP |
237 | if (ret) |
238 | /* on error: disable all previously enabled clks */ | |
239 | fsl_disable_clocks(fsl_edma, i); | |
d6be34fb JL |
240 | |
241 | } | |
242 | ||
d6be34fb JL |
243 | fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); |
244 | ||
245 | INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); | |
246 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
247 | struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; | |
248 | ||
249 | fsl_chan->edma = fsl_edma; | |
82d149b8 YY |
250 | fsl_chan->pm_state = RUNNING; |
251 | fsl_chan->slave_id = 0; | |
252 | fsl_chan->idle = true; | |
0fa89f97 | 253 | fsl_chan->dma_dir = DMA_NONE; |
d6be34fb JL |
254 | fsl_chan->vchan.desc_free = fsl_edma_free_desc; |
255 | vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); | |
256 | ||
377eaf3b | 257 | edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); |
d6be34fb JL |
258 | fsl_edma_chan_mux(fsl_chan, 0, false); |
259 | } | |
260 | ||
377eaf3b | 261 | edma_writel(fsl_edma, ~0, regs->intl); |
0fe25d61 SA |
262 | ret = fsl_edma_irq_init(pdev, fsl_edma); |
263 | if (ret) | |
264 | return ret; | |
265 | ||
d6be34fb JL |
266 | dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); |
267 | dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); | |
268 | dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask); | |
269 | ||
270 | fsl_edma->dma_dev.dev = &pdev->dev; | |
271 | fsl_edma->dma_dev.device_alloc_chan_resources | |
272 | = fsl_edma_alloc_chan_resources; | |
273 | fsl_edma->dma_dev.device_free_chan_resources | |
274 | = fsl_edma_free_chan_resources; | |
275 | fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status; | |
276 | fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; | |
277 | fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic; | |
d80f381f MR |
278 | fsl_edma->dma_dev.device_config = fsl_edma_slave_config; |
279 | fsl_edma->dma_dev.device_pause = fsl_edma_pause; | |
280 | fsl_edma->dma_dev.device_resume = fsl_edma_resume; | |
281 | fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; | |
d6be34fb | 282 | fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; |
f45c4311 MR |
283 | |
284 | fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; | |
285 | fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; | |
286 | fsl_edma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); | |
d6be34fb JL |
287 | |
288 | platform_set_drvdata(pdev, fsl_edma); | |
289 | ||
290 | ret = dma_async_device_register(&fsl_edma->dma_dev); | |
291 | if (ret) { | |
a86144da PG |
292 | dev_err(&pdev->dev, |
293 | "Can't register Freescale eDMA engine. (%d)\n", ret); | |
2610acf4 | 294 | fsl_disable_clocks(fsl_edma, DMAMUX_NR); |
d6be34fb JL |
295 | return ret; |
296 | } | |
297 | ||
298 | ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma); | |
299 | if (ret) { | |
a86144da PG |
300 | dev_err(&pdev->dev, |
301 | "Can't register Freescale eDMA of_dma. (%d)\n", ret); | |
d6be34fb | 302 | dma_async_device_unregister(&fsl_edma->dma_dev); |
2610acf4 | 303 | fsl_disable_clocks(fsl_edma, DMAMUX_NR); |
d6be34fb JL |
304 | return ret; |
305 | } | |
306 | ||
307 | /* enable round robin arbitration */ | |
377eaf3b | 308 | edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); |
d6be34fb JL |
309 | |
310 | return 0; | |
311 | } | |
312 | ||
313 | static int fsl_edma_remove(struct platform_device *pdev) | |
314 | { | |
315 | struct device_node *np = pdev->dev.of_node; | |
316 | struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev); | |
d6be34fb | 317 | |
476c7c80 | 318 | fsl_edma_irq_exit(pdev, fsl_edma); |
6f93b93b | 319 | fsl_edma_cleanup_vchan(&fsl_edma->dma_dev); |
d6be34fb JL |
320 | of_dma_controller_free(np); |
321 | dma_async_device_unregister(&fsl_edma->dma_dev); | |
2610acf4 | 322 | fsl_disable_clocks(fsl_edma, DMAMUX_NR); |
d6be34fb JL |
323 | |
324 | return 0; | |
325 | } | |
326 | ||
82d149b8 YY |
327 | static int fsl_edma_suspend_late(struct device *dev) |
328 | { | |
329 | struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); | |
330 | struct fsl_edma_chan *fsl_chan; | |
331 | unsigned long flags; | |
332 | int i; | |
333 | ||
334 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
335 | fsl_chan = &fsl_edma->chans[i]; | |
336 | spin_lock_irqsave(&fsl_chan->vchan.lock, flags); | |
337 | /* Make sure chan is idle or will force disable. */ | |
338 | if (unlikely(!fsl_chan->idle)) { | |
339 | dev_warn(dev, "WARN: There is non-idle channel."); | |
340 | fsl_edma_disable_request(fsl_chan); | |
341 | fsl_edma_chan_mux(fsl_chan, 0, false); | |
342 | } | |
343 | ||
344 | fsl_chan->pm_state = SUSPENDED; | |
345 | spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); | |
346 | } | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
351 | static int fsl_edma_resume_early(struct device *dev) | |
352 | { | |
353 | struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev); | |
354 | struct fsl_edma_chan *fsl_chan; | |
377eaf3b | 355 | struct edma_regs *regs = &fsl_edma->regs; |
82d149b8 YY |
356 | int i; |
357 | ||
358 | for (i = 0; i < fsl_edma->n_chans; i++) { | |
359 | fsl_chan = &fsl_edma->chans[i]; | |
360 | fsl_chan->pm_state = RUNNING; | |
377eaf3b | 361 | edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); |
82d149b8 YY |
362 | if (fsl_chan->slave_id != 0) |
363 | fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); | |
364 | } | |
365 | ||
377eaf3b | 366 | edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); |
82d149b8 YY |
367 | |
368 | return 0; | |
369 | } | |
370 | ||
371 | /* | |
372 | * eDMA provides the service to others, so it should be suspend late | |
373 | * and resume early. When eDMA suspend, all of the clients should stop | |
374 | * the DMA data transmission and let the channel idle. | |
375 | */ | |
376 | static const struct dev_pm_ops fsl_edma_pm_ops = { | |
377 | .suspend_late = fsl_edma_suspend_late, | |
378 | .resume_early = fsl_edma_resume_early, | |
379 | }; | |
380 | ||
d6be34fb JL |
381 | static const struct of_device_id fsl_edma_dt_ids[] = { |
382 | { .compatible = "fsl,vf610-edma", }, | |
383 | { /* sentinel */ } | |
384 | }; | |
385 | MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); | |
386 | ||
387 | static struct platform_driver fsl_edma_driver = { | |
388 | .driver = { | |
389 | .name = "fsl-edma", | |
d6be34fb | 390 | .of_match_table = fsl_edma_dt_ids, |
82d149b8 | 391 | .pm = &fsl_edma_pm_ops, |
d6be34fb JL |
392 | }, |
393 | .probe = fsl_edma_probe, | |
394 | .remove = fsl_edma_remove, | |
395 | }; | |
396 | ||
8edc51c1 YY |
397 | static int __init fsl_edma_init(void) |
398 | { | |
399 | return platform_driver_register(&fsl_edma_driver); | |
400 | } | |
401 | subsys_initcall(fsl_edma_init); | |
402 | ||
403 | static void __exit fsl_edma_exit(void) | |
404 | { | |
405 | platform_driver_unregister(&fsl_edma_driver); | |
406 | } | |
407 | module_exit(fsl_edma_exit); | |
d6be34fb JL |
408 | |
409 | MODULE_ALIAS("platform:fsl-edma"); | |
410 | MODULE_DESCRIPTION("Freescale eDMA engine driver"); | |
411 | MODULE_LICENSE("GPL v2"); |