]>
Commit | Line | Data |
---|---|---|
9cade1a4 AS |
1 | /* |
2 | * Platform driver for the Synopsys DesignWare DMA Controller | |
3 | * | |
4 | * Copyright (C) 2007-2008 Atmel Corporation | |
5 | * Copyright (C) 2010-2011 ST Microelectronics | |
6 | * Copyright (C) 2013 Intel Corporation | |
7 | * | |
8 | * Some parts of this driver are derived from the original dw_dmac. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/module.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/clk.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/dmaengine.h> | |
20 | #include <linux/dma-mapping.h> | |
21 | #include <linux/of.h> | |
22 | #include <linux/of_dma.h> | |
23 | #include <linux/acpi.h> | |
24 | #include <linux/acpi_dma.h> | |
25 | ||
26 | #include "internal.h" | |
27 | ||
28 | struct dw_dma_of_filter_args { | |
29 | struct dw_dma *dw; | |
30 | unsigned int req; | |
31 | unsigned int src; | |
32 | unsigned int dst; | |
33 | }; | |
34 | ||
35 | static bool dw_dma_of_filter(struct dma_chan *chan, void *param) | |
36 | { | |
37 | struct dw_dma_chan *dwc = to_dw_dma_chan(chan); | |
38 | struct dw_dma_of_filter_args *fargs = param; | |
39 | ||
40 | /* Ensure the device matches our channel */ | |
41 | if (chan->device != &fargs->dw->dma) | |
42 | return false; | |
43 | ||
44 | dwc->request_line = fargs->req; | |
45 | dwc->src_master = fargs->src; | |
46 | dwc->dst_master = fargs->dst; | |
47 | ||
48 | return true; | |
49 | } | |
50 | ||
51 | static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, | |
52 | struct of_dma *ofdma) | |
53 | { | |
54 | struct dw_dma *dw = ofdma->of_dma_data; | |
55 | struct dw_dma_of_filter_args fargs = { | |
56 | .dw = dw, | |
57 | }; | |
58 | dma_cap_mask_t cap; | |
59 | ||
60 | if (dma_spec->args_count != 3) | |
61 | return NULL; | |
62 | ||
63 | fargs.req = dma_spec->args[0]; | |
64 | fargs.src = dma_spec->args[1]; | |
65 | fargs.dst = dma_spec->args[2]; | |
66 | ||
67 | if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || | |
68 | fargs.src >= dw->nr_masters || | |
69 | fargs.dst >= dw->nr_masters)) | |
70 | return NULL; | |
71 | ||
72 | dma_cap_zero(cap); | |
73 | dma_cap_set(DMA_SLAVE, cap); | |
74 | ||
75 | /* TODO: there should be a simpler way to do this */ | |
76 | return dma_request_channel(cap, dw_dma_of_filter, &fargs); | |
77 | } | |
78 | ||
79 | #ifdef CONFIG_ACPI | |
80 | static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) | |
81 | { | |
82 | struct dw_dma_chan *dwc = to_dw_dma_chan(chan); | |
83 | struct acpi_dma_spec *dma_spec = param; | |
84 | ||
85 | if (chan->device->dev != dma_spec->dev || | |
86 | chan->chan_id != dma_spec->chan_id) | |
87 | return false; | |
88 | ||
89 | dwc->request_line = dma_spec->slave_id; | |
90 | dwc->src_master = dwc_get_sms(NULL); | |
91 | dwc->dst_master = dwc_get_dms(NULL); | |
92 | ||
93 | return true; | |
94 | } | |
95 | ||
96 | static void dw_dma_acpi_controller_register(struct dw_dma *dw) | |
97 | { | |
98 | struct device *dev = dw->dma.dev; | |
99 | struct acpi_dma_filter_info *info; | |
100 | int ret; | |
101 | ||
102 | info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); | |
103 | if (!info) | |
104 | return; | |
105 | ||
106 | dma_cap_zero(info->dma_cap); | |
107 | dma_cap_set(DMA_SLAVE, info->dma_cap); | |
108 | info->filter_fn = dw_dma_acpi_filter; | |
109 | ||
110 | ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, | |
111 | info); | |
112 | if (ret) | |
113 | dev_err(dev, "could not register acpi_dma_controller\n"); | |
114 | } | |
115 | #else /* !CONFIG_ACPI */ | |
116 | static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} | |
117 | #endif /* !CONFIG_ACPI */ | |
118 | ||
119 | #ifdef CONFIG_OF | |
120 | static struct dw_dma_platform_data * | |
121 | dw_dma_parse_dt(struct platform_device *pdev) | |
122 | { | |
123 | struct device_node *np = pdev->dev.of_node; | |
124 | struct dw_dma_platform_data *pdata; | |
125 | u32 tmp, arr[4]; | |
126 | ||
127 | if (!np) { | |
128 | dev_err(&pdev->dev, "Missing DT data\n"); | |
129 | return NULL; | |
130 | } | |
131 | ||
132 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
133 | if (!pdata) | |
134 | return NULL; | |
135 | ||
136 | if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) | |
137 | return NULL; | |
138 | ||
139 | if (of_property_read_bool(np, "is_private")) | |
140 | pdata->is_private = true; | |
141 | ||
142 | if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) | |
143 | pdata->chan_allocation_order = (unsigned char)tmp; | |
144 | ||
145 | if (!of_property_read_u32(np, "chan_priority", &tmp)) | |
146 | pdata->chan_priority = tmp; | |
147 | ||
148 | if (!of_property_read_u32(np, "block_size", &tmp)) | |
149 | pdata->block_size = tmp; | |
150 | ||
151 | if (!of_property_read_u32(np, "dma-masters", &tmp)) { | |
152 | if (tmp > 4) | |
153 | return NULL; | |
154 | ||
155 | pdata->nr_masters = tmp; | |
156 | } | |
157 | ||
158 | if (!of_property_read_u32_array(np, "data_width", arr, | |
159 | pdata->nr_masters)) | |
160 | for (tmp = 0; tmp < pdata->nr_masters; tmp++) | |
161 | pdata->data_width[tmp] = arr[tmp]; | |
162 | ||
163 | return pdata; | |
164 | } | |
165 | #else | |
166 | static inline struct dw_dma_platform_data * | |
167 | dw_dma_parse_dt(struct platform_device *pdev) | |
168 | { | |
169 | return NULL; | |
170 | } | |
171 | #endif | |
172 | ||
173 | static int dw_probe(struct platform_device *pdev) | |
174 | { | |
175 | struct dw_dma_chip *chip; | |
176 | struct device *dev = &pdev->dev; | |
177 | struct resource *mem; | |
178 | struct dw_dma_platform_data *pdata; | |
179 | int err; | |
180 | ||
181 | chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); | |
182 | if (!chip) | |
183 | return -ENOMEM; | |
184 | ||
185 | chip->irq = platform_get_irq(pdev, 0); | |
186 | if (chip->irq < 0) | |
187 | return chip->irq; | |
188 | ||
189 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
190 | chip->regs = devm_ioremap_resource(dev, mem); | |
191 | if (IS_ERR(chip->regs)) | |
192 | return PTR_ERR(chip->regs); | |
193 | ||
194 | /* Apply default dma_mask if needed */ | |
195 | if (!dev->dma_mask) { | |
196 | dev->dma_mask = &dev->coherent_dma_mask; | |
197 | dev->coherent_dma_mask = DMA_BIT_MASK(32); | |
198 | } | |
199 | ||
200 | pdata = dev_get_platdata(dev); | |
201 | if (!pdata) | |
202 | pdata = dw_dma_parse_dt(pdev); | |
203 | ||
204 | chip->dev = dev; | |
205 | ||
206 | err = dw_dma_probe(chip, pdata); | |
207 | if (err) | |
208 | return err; | |
209 | ||
210 | platform_set_drvdata(pdev, chip); | |
211 | ||
212 | if (pdev->dev.of_node) { | |
213 | err = of_dma_controller_register(pdev->dev.of_node, | |
214 | dw_dma_of_xlate, chip->dw); | |
215 | if (err) | |
216 | dev_err(&pdev->dev, | |
217 | "could not register of_dma_controller\n"); | |
218 | } | |
219 | ||
220 | if (ACPI_HANDLE(&pdev->dev)) | |
221 | dw_dma_acpi_controller_register(chip->dw); | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int dw_remove(struct platform_device *pdev) | |
227 | { | |
228 | struct dw_dma_chip *chip = platform_get_drvdata(pdev); | |
229 | ||
230 | if (pdev->dev.of_node) | |
231 | of_dma_controller_free(pdev->dev.of_node); | |
232 | ||
233 | return dw_dma_remove(chip); | |
234 | } | |
235 | ||
236 | static void dw_shutdown(struct platform_device *pdev) | |
237 | { | |
238 | struct dw_dma_chip *chip = platform_get_drvdata(pdev); | |
239 | ||
240 | dw_dma_shutdown(chip); | |
241 | } | |
242 | ||
243 | #ifdef CONFIG_OF | |
244 | static const struct of_device_id dw_dma_of_id_table[] = { | |
245 | { .compatible = "snps,dma-spear1340" }, | |
246 | {} | |
247 | }; | |
248 | MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); | |
249 | #endif | |
250 | ||
251 | #ifdef CONFIG_ACPI | |
252 | static const struct acpi_device_id dw_dma_acpi_id_table[] = { | |
253 | { "INTL9C60", 0 }, | |
254 | { } | |
255 | }; | |
256 | #endif | |
257 | ||
258 | #ifdef CONFIG_PM_SLEEP | |
259 | ||
260 | static int dw_suspend_noirq(struct device *dev) | |
261 | { | |
262 | struct platform_device *pdev = to_platform_device(dev); | |
263 | struct dw_dma_chip *chip = platform_get_drvdata(pdev); | |
264 | ||
265 | return dw_dma_suspend(chip); | |
266 | } | |
267 | ||
268 | static int dw_resume_noirq(struct device *dev) | |
269 | { | |
270 | struct platform_device *pdev = to_platform_device(dev); | |
271 | struct dw_dma_chip *chip = platform_get_drvdata(pdev); | |
272 | ||
273 | return dw_dma_resume(chip); | |
274 | } | |
275 | ||
276 | #else /* !CONFIG_PM_SLEEP */ | |
277 | ||
278 | #define dw_suspend_noirq NULL | |
279 | #define dw_resume_noirq NULL | |
280 | ||
281 | #endif /* !CONFIG_PM_SLEEP */ | |
282 | ||
283 | static const struct dev_pm_ops dw_dev_pm_ops = { | |
284 | .suspend_noirq = dw_suspend_noirq, | |
285 | .resume_noirq = dw_resume_noirq, | |
286 | .freeze_noirq = dw_suspend_noirq, | |
287 | .thaw_noirq = dw_resume_noirq, | |
288 | .restore_noirq = dw_resume_noirq, | |
289 | .poweroff_noirq = dw_suspend_noirq, | |
290 | }; | |
291 | ||
292 | static struct platform_driver dw_driver = { | |
293 | .probe = dw_probe, | |
294 | .remove = dw_remove, | |
295 | .shutdown = dw_shutdown, | |
296 | .driver = { | |
297 | .name = "dw_dmac", | |
298 | .pm = &dw_dev_pm_ops, | |
299 | .of_match_table = of_match_ptr(dw_dma_of_id_table), | |
300 | .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), | |
301 | }, | |
302 | }; | |
303 | ||
304 | static int __init dw_init(void) | |
305 | { | |
306 | return platform_driver_register(&dw_driver); | |
307 | } | |
308 | subsys_initcall(dw_init); | |
309 | ||
310 | static void __exit dw_exit(void) | |
311 | { | |
312 | platform_driver_unregister(&dw_driver); | |
313 | } | |
314 | module_exit(dw_exit); | |
315 | ||
316 | MODULE_LICENSE("GPL v2"); | |
317 | MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); |