1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Copyright (c) 2013-2014 Freescale Semiconductor, Inc |
4 | // Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it> |
5 | |
6 | #include <linux/module.h> |
7 | #include <linux/interrupt.h> |
8 | #include <linux/dmaengine.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/platform_data/dma-mcf-edma.h> |
11 | |
12 | #include "fsl-edma-common.h" |
13 | |
14 | #define EDMA_CHANNELS 64 |
15 | #define EDMA_MASK_CH(x) ((x) & GENMASK(5, 0)) |
16 | |
17 | static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id) |
18 | { |
19 | struct fsl_edma_engine *mcf_edma = dev_id; |
20 | struct edma_regs *regs = &mcf_edma->regs; |
21 | unsigned int ch; |
22 | u64 intmap; |
23 | |
24 | intmap = ioread32(regs->inth); |
25 | intmap <<= 32; |
26 | intmap |= ioread32(regs->intl); |
27 | if (!intmap) |
28 | return IRQ_NONE; |
29 | |
30 | for (ch = 0; ch < mcf_edma->n_chans; ch++) { |
31 | if (intmap & BIT(ch)) { |
32 | iowrite8(EDMA_MASK_CH(ch), regs->cint); |
33 | fsl_edma_tx_chan_handler(fsl_chan: &mcf_edma->chans[ch]); |
34 | } |
35 | } |
36 | |
37 | return IRQ_HANDLED; |
38 | } |
39 | |
40 | static irqreturn_t mcf_edma_err_handler(int irq, void *dev_id) |
41 | { |
42 | struct fsl_edma_engine *mcf_edma = dev_id; |
43 | struct edma_regs *regs = &mcf_edma->regs; |
44 | unsigned int err, ch; |
45 | |
46 | err = ioread32(regs->errl); |
47 | if (!err) |
48 | return IRQ_NONE; |
49 | |
50 | for (ch = 0; ch < (EDMA_CHANNELS / 2); ch++) { |
51 | if (err & BIT(ch)) { |
52 | fsl_edma_disable_request(fsl_chan: &mcf_edma->chans[ch]); |
53 | iowrite8(EDMA_CERR_CERR(ch), regs->cerr); |
54 | fsl_edma_err_chan_handler(fsl_chan: &mcf_edma->chans[ch]); |
55 | } |
56 | } |
57 | |
58 | err = ioread32(regs->errh); |
59 | if (!err) |
60 | return IRQ_NONE; |
61 | |
62 | for (ch = (EDMA_CHANNELS / 2); ch < EDMA_CHANNELS; ch++) { |
63 | if (err & (BIT(ch - (EDMA_CHANNELS / 2)))) { |
64 | fsl_edma_disable_request(fsl_chan: &mcf_edma->chans[ch]); |
65 | iowrite8(EDMA_CERR_CERR(ch), regs->cerr); |
66 | mcf_edma->chans[ch].status = DMA_ERROR; |
67 | mcf_edma->chans[ch].idle = true; |
68 | } |
69 | } |
70 | |
71 | return IRQ_HANDLED; |
72 | } |
73 | |
74 | static int mcf_edma_irq_init(struct platform_device *pdev, |
75 | struct fsl_edma_engine *mcf_edma) |
76 | { |
77 | int ret = 0, i; |
78 | struct resource *res; |
79 | |
80 | res = platform_get_resource_byname(pdev, |
81 | IORESOURCE_IRQ, "edma-tx-00-15" ); |
82 | if (!res) |
83 | return -1; |
84 | |
85 | for (ret = 0, i = res->start; i <= res->end; ++i) |
86 | ret |= request_irq(irq: i, handler: mcf_edma_tx_handler, flags: 0, name: "eDMA" , dev: mcf_edma); |
87 | if (ret) |
88 | return ret; |
89 | |
90 | res = platform_get_resource_byname(pdev, |
91 | IORESOURCE_IRQ, "edma-tx-16-55" ); |
92 | if (!res) |
93 | return -1; |
94 | |
95 | for (ret = 0, i = res->start; i <= res->end; ++i) |
96 | ret |= request_irq(irq: i, handler: mcf_edma_tx_handler, flags: 0, name: "eDMA" , dev: mcf_edma); |
97 | if (ret) |
98 | return ret; |
99 | |
100 | ret = platform_get_irq_byname(pdev, "edma-tx-56-63" ); |
101 | if (ret != -ENXIO) { |
102 | ret = request_irq(irq: ret, handler: mcf_edma_tx_handler, |
103 | flags: 0, name: "eDMA" , dev: mcf_edma); |
104 | if (ret) |
105 | return ret; |
106 | } |
107 | |
108 | ret = platform_get_irq_byname(pdev, "edma-err" ); |
109 | if (ret != -ENXIO) { |
110 | ret = request_irq(irq: ret, handler: mcf_edma_err_handler, |
111 | flags: 0, name: "eDMA" , dev: mcf_edma); |
112 | if (ret) |
113 | return ret; |
114 | } |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static void mcf_edma_irq_free(struct platform_device *pdev, |
120 | struct fsl_edma_engine *mcf_edma) |
121 | { |
122 | int irq; |
123 | struct resource *res; |
124 | |
125 | res = platform_get_resource_byname(pdev, |
126 | IORESOURCE_IRQ, "edma-tx-00-15" ); |
127 | if (res) { |
128 | for (irq = res->start; irq <= res->end; irq++) |
129 | free_irq(irq, mcf_edma); |
130 | } |
131 | |
132 | res = platform_get_resource_byname(pdev, |
133 | IORESOURCE_IRQ, "edma-tx-16-55" ); |
134 | if (res) { |
135 | for (irq = res->start; irq <= res->end; irq++) |
136 | free_irq(irq, mcf_edma); |
137 | } |
138 | |
139 | irq = platform_get_irq_byname(pdev, "edma-tx-56-63" ); |
140 | if (irq != -ENXIO) |
141 | free_irq(irq, mcf_edma); |
142 | |
143 | irq = platform_get_irq_byname(pdev, "edma-err" ); |
144 | if (irq != -ENXIO) |
145 | free_irq(irq, mcf_edma); |
146 | } |
147 | |
148 | static struct fsl_edma_drvdata mcf_data = { |
149 | .flags = FSL_EDMA_DRV_EDMA64, |
150 | .setup_irq = mcf_edma_irq_init, |
151 | }; |
152 | |
153 | static int mcf_edma_probe(struct platform_device *pdev) |
154 | { |
155 | struct mcf_edma_platform_data *pdata; |
156 | struct fsl_edma_engine *mcf_edma; |
157 | struct edma_regs *regs; |
158 | int ret, i, chans; |
159 | |
160 | pdata = dev_get_platdata(dev: &pdev->dev); |
161 | if (!pdata) { |
162 | dev_err(&pdev->dev, "no platform data supplied\n" ); |
163 | return -EINVAL; |
164 | } |
165 | |
166 | if (!pdata->dma_channels) { |
167 | dev_info(&pdev->dev, "setting default channel number to 64" ); |
168 | chans = 64; |
169 | } else { |
170 | chans = pdata->dma_channels; |
171 | } |
172 | |
173 | mcf_edma = devm_kzalloc(dev: &pdev->dev, struct_size(mcf_edma, chans, chans), |
174 | GFP_KERNEL); |
175 | if (!mcf_edma) |
176 | return -ENOMEM; |
177 | |
178 | mcf_edma->n_chans = chans; |
179 | |
180 | /* Set up drvdata for ColdFire edma */ |
181 | mcf_edma->drvdata = &mcf_data; |
182 | mcf_edma->big_endian = 1; |
183 | |
184 | mutex_init(&mcf_edma->fsl_edma_mutex); |
185 | |
186 | mcf_edma->membase = devm_platform_ioremap_resource(pdev, index: 0); |
187 | if (IS_ERR(ptr: mcf_edma->membase)) |
188 | return PTR_ERR(ptr: mcf_edma->membase); |
189 | |
190 | fsl_edma_setup_regs(edma: mcf_edma); |
191 | regs = &mcf_edma->regs; |
192 | |
193 | INIT_LIST_HEAD(list: &mcf_edma->dma_dev.channels); |
194 | for (i = 0; i < mcf_edma->n_chans; i++) { |
195 | struct fsl_edma_chan *mcf_chan = &mcf_edma->chans[i]; |
196 | |
197 | mcf_chan->edma = mcf_edma; |
198 | mcf_chan->slave_id = i; |
199 | mcf_chan->idle = true; |
200 | mcf_chan->dma_dir = DMA_NONE; |
201 | mcf_chan->vchan.desc_free = fsl_edma_free_desc; |
202 | vchan_init(vc: &mcf_chan->vchan, dmadev: &mcf_edma->dma_dev); |
203 | mcf_chan->tcd = mcf_edma->membase + EDMA_TCD |
204 | + i * sizeof(struct fsl_edma_hw_tcd); |
205 | iowrite32(0x0, &mcf_chan->tcd->csr); |
206 | } |
207 | |
208 | iowrite32(~0, regs->inth); |
209 | iowrite32(~0, regs->intl); |
210 | |
211 | ret = mcf_edma->drvdata->setup_irq(pdev, mcf_edma); |
212 | if (ret) |
213 | return ret; |
214 | |
215 | dma_cap_set(DMA_PRIVATE, mcf_edma->dma_dev.cap_mask); |
216 | dma_cap_set(DMA_SLAVE, mcf_edma->dma_dev.cap_mask); |
217 | dma_cap_set(DMA_CYCLIC, mcf_edma->dma_dev.cap_mask); |
218 | |
219 | mcf_edma->dma_dev.dev = &pdev->dev; |
220 | mcf_edma->dma_dev.device_alloc_chan_resources = |
221 | fsl_edma_alloc_chan_resources; |
222 | mcf_edma->dma_dev.device_free_chan_resources = |
223 | fsl_edma_free_chan_resources; |
224 | mcf_edma->dma_dev.device_config = fsl_edma_slave_config; |
225 | mcf_edma->dma_dev.device_prep_dma_cyclic = |
226 | fsl_edma_prep_dma_cyclic; |
227 | mcf_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; |
228 | mcf_edma->dma_dev.device_tx_status = fsl_edma_tx_status; |
229 | mcf_edma->dma_dev.device_pause = fsl_edma_pause; |
230 | mcf_edma->dma_dev.device_resume = fsl_edma_resume; |
231 | mcf_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all; |
232 | mcf_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; |
233 | |
234 | mcf_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; |
235 | mcf_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; |
236 | mcf_edma->dma_dev.directions = |
237 | BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); |
238 | |
239 | mcf_edma->dma_dev.filter.fn = mcf_edma_filter_fn; |
240 | mcf_edma->dma_dev.filter.map = pdata->slave_map; |
241 | mcf_edma->dma_dev.filter.mapcnt = pdata->slavecnt; |
242 | |
243 | platform_set_drvdata(pdev, data: mcf_edma); |
244 | |
245 | ret = dma_async_device_register(device: &mcf_edma->dma_dev); |
246 | if (ret) { |
247 | dev_err(&pdev->dev, |
248 | "Can't register Freescale eDMA engine. (%d)\n" , ret); |
249 | return ret; |
250 | } |
251 | |
252 | /* Enable round robin arbitration */ |
253 | iowrite32(EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static void mcf_edma_remove(struct platform_device *pdev) |
259 | { |
260 | struct fsl_edma_engine *mcf_edma = platform_get_drvdata(pdev); |
261 | |
262 | mcf_edma_irq_free(pdev, mcf_edma); |
263 | fsl_edma_cleanup_vchan(dmadev: &mcf_edma->dma_dev); |
264 | dma_async_device_unregister(device: &mcf_edma->dma_dev); |
265 | } |
266 | |
267 | static struct platform_driver mcf_edma_driver = { |
268 | .driver = { |
269 | .name = "mcf-edma" , |
270 | }, |
271 | .probe = mcf_edma_probe, |
272 | .remove_new = mcf_edma_remove, |
273 | }; |
274 | |
275 | bool mcf_edma_filter_fn(struct dma_chan *chan, void *param) |
276 | { |
277 | if (chan->device->dev->driver == &mcf_edma_driver.driver) { |
278 | struct fsl_edma_chan *mcf_chan = to_fsl_edma_chan(chan); |
279 | |
280 | return (mcf_chan->slave_id == (uintptr_t)param); |
281 | } |
282 | |
283 | return false; |
284 | } |
285 | EXPORT_SYMBOL(mcf_edma_filter_fn); |
286 | |
287 | static int __init mcf_edma_init(void) |
288 | { |
289 | return platform_driver_register(&mcf_edma_driver); |
290 | } |
291 | subsys_initcall(mcf_edma_init); |
292 | |
293 | static void __exit mcf_edma_exit(void) |
294 | { |
295 | platform_driver_unregister(&mcf_edma_driver); |
296 | } |
297 | module_exit(mcf_edma_exit); |
298 | |
299 | MODULE_ALIAS("platform:mcf-edma" ); |
300 | MODULE_DESCRIPTION("Freescale eDMA engine driver, ColdFire family" ); |
301 | MODULE_LICENSE("GPL v2" ); |
302 | |