aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>2013-04-09 07:05:43 -0400
committerVinod Koul <vinod.koul@intel.com>2013-04-15 12:34:10 -0400
commit1b2e98bc1e35ebe1f65c3db62c8317096ad7f2c8 (patch)
tree326e1f7e6f311f1107f27eb048d521e880944360 /drivers
parenteceec44ecd7f3285468a684e7216df2316b178f3 (diff)
dma: acpi-dma: introduce ACPI DMA helpers
There is a new generic API to get a DMA channel for a slave device (commit 9a6cecc8 "dmaengine: add helper function to request a slave DMA channel"). In similar fashion to the DT case (commit aa3da644 "of: Add generic device tree DMA helpers") we introduce helpers to the DMAC drivers which are enumerated by ACPI. The proposed extension provides the following API calls: acpi_dma_controller_register(), devm_acpi_dma_controller_register() acpi_dma_controller_free(), devm_acpi_dma_controller_free() acpi_dma_simple_xlate() acpi_dma_request_slave_chan_by_index() acpi_dma_request_slave_chan_by_name() The first two should be used, for example, at probe() and remove() of the corresponding DMAC driver. At the register stage the DMAC driver supplies a custom xlate() function to translate a struct dma_spec into struct dma_chan. Accordingly to the ACPI Fixed DMA resource specification the only two pieces of information the slave device has are the channel id and the request line (slave id). Those two are represented by struct dma_spec. The acpi_dma_request_slave_chan_by_index() provides access to the specifix FixedDMA resource by its index. Whereas dma_request_slave_channel() takes a string parameter to identify the DMA resources required by the slave device. To make a slave device driver work with both DeviceTree and ACPI enumeration a simple convention is established: "tx" corresponds to the index 0 and "rx" to the index 1. In case of robust configuration the slave device driver unfortunately needs to call acpi_dma_request_slave_chan_by_index() directly. Additionally the patch provides "managed" version of the register/free pair i.e. devm_acpi_dma_controller_register() and devm_acpi_dma_controller_free(). Usually, the driver uses only devm_acpi_dma_controller_register(). Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/dma/Kconfig4
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/acpi-dma.c279
3 files changed, 284 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index d5c58e839b27..afe5b1958382 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -326,6 +326,10 @@ config DMA_ENGINE
326config DMA_VIRTUAL_CHANNELS 326config DMA_VIRTUAL_CHANNELS
327 tristate 327 tristate
328 328
329config DMA_ACPI
330 def_bool y
331 depends on ACPI
332
329config DMA_OF 333config DMA_OF
330 def_bool y 334 def_bool y
331 depends on OF 335 depends on OF
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 488e3ff85b52..268e62634bca 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -3,6 +3,7 @@ ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
3 3
4obj-$(CONFIG_DMA_ENGINE) += dmaengine.o 4obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
5obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o 5obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
6obj-$(CONFIG_DMA_ACPI) += acpi-dma.o
6obj-$(CONFIG_DMA_OF) += of-dma.o 7obj-$(CONFIG_DMA_OF) += of-dma.o
7 8
8obj-$(CONFIG_NET_DMA) += iovlock.o 9obj-$(CONFIG_NET_DMA) += iovlock.o
diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c
new file mode 100644
index 000000000000..ba6fc62e9651
--- /dev/null
+++ b/drivers/dma/acpi-dma.c
@@ -0,0 +1,279 @@
1/*
2 * ACPI helpers for DMA request / controller
3 *
4 * Based on of-dma.c
5 *
6 * Copyright (C) 2013, Intel Corporation
7 * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/device.h>
15#include <linux/module.h>
16#include <linux/list.h>
17#include <linux/mutex.h>
18#include <linux/slab.h>
19#include <linux/acpi.h>
20#include <linux/acpi_dma.h>
21
22static LIST_HEAD(acpi_dma_list);
23static DEFINE_MUTEX(acpi_dma_lock);
24
25/**
26 * acpi_dma_controller_register - Register a DMA controller to ACPI DMA helpers
27 * @dev: struct device of DMA controller
28 * @acpi_dma_xlate: translation function which converts a dma specifier
29 * into a dma_chan structure
30 * @data pointer to controller specific data to be used by
31 * translation function
32 *
33 * Returns 0 on success or appropriate errno value on error.
34 *
35 * Allocated memory should be freed with appropriate acpi_dma_controller_free()
36 * call.
37 */
38int acpi_dma_controller_register(struct device *dev,
39 struct dma_chan *(*acpi_dma_xlate)
40 (struct acpi_dma_spec *, struct acpi_dma *),
41 void *data)
42{
43 struct acpi_device *adev;
44 struct acpi_dma *adma;
45
46 if (!dev || !acpi_dma_xlate)
47 return -EINVAL;
48
49 /* Check if the device was enumerated by ACPI */
50 if (!ACPI_HANDLE(dev))
51 return -EINVAL;
52
53 if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev))
54 return -EINVAL;
55
56 adma = kzalloc(sizeof(*adma), GFP_KERNEL);
57 if (!adma)
58 return -ENOMEM;
59
60 adma->dev = dev;
61 adma->acpi_dma_xlate = acpi_dma_xlate;
62 adma->data = data;
63
64 /* Now queue acpi_dma controller structure in list */
65 mutex_lock(&acpi_dma_lock);
66 list_add_tail(&adma->dma_controllers, &acpi_dma_list);
67 mutex_unlock(&acpi_dma_lock);
68
69 return 0;
70}
71EXPORT_SYMBOL_GPL(acpi_dma_controller_register);
72
73/**
74 * acpi_dma_controller_free - Remove a DMA controller from ACPI DMA helpers list
75 * @dev: struct device of DMA controller
76 *
77 * Memory allocated by acpi_dma_controller_register() is freed here.
78 */
79int acpi_dma_controller_free(struct device *dev)
80{
81 struct acpi_dma *adma;
82
83 if (!dev)
84 return -EINVAL;
85
86 mutex_lock(&acpi_dma_lock);
87
88 list_for_each_entry(adma, &acpi_dma_list, dma_controllers)
89 if (adma->dev == dev) {
90 list_del(&adma->dma_controllers);
91 mutex_unlock(&acpi_dma_lock);
92 kfree(adma);
93 return 0;
94 }
95
96 mutex_unlock(&acpi_dma_lock);
97 return -ENODEV;
98}
99EXPORT_SYMBOL_GPL(acpi_dma_controller_free);
100
101static void devm_acpi_dma_release(struct device *dev, void *res)
102{
103 acpi_dma_controller_free(dev);
104}
105
106/**
107 * devm_acpi_dma_controller_register - resource managed acpi_dma_controller_register()
108 * @dev: device that is registering this DMA controller
109 * @acpi_dma_xlate: translation function
110 * @data pointer to controller specific data
111 *
112 * Managed acpi_dma_controller_register(). DMA controller registered by this
113 * function are automatically freed on driver detach. See
114 * acpi_dma_controller_register() for more information.
115 */
116int devm_acpi_dma_controller_register(struct device *dev,
117 struct dma_chan *(*acpi_dma_xlate)
118 (struct acpi_dma_spec *, struct acpi_dma *),
119 void *data)
120{
121 void *res;
122 int ret;
123
124 res = devres_alloc(devm_acpi_dma_release, 0, GFP_KERNEL);
125 if (!res)
126 return -ENOMEM;
127
128 ret = acpi_dma_controller_register(dev, acpi_dma_xlate, data);
129 if (ret) {
130 devres_free(res);
131 return ret;
132 }
133 devres_add(dev, res);
134 return 0;
135}
136EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register);
137
138/**
139 * devm_acpi_dma_controller_free - resource managed acpi_dma_controller_free()
140 *
141 * Unregister a DMA controller registered with
142 * devm_acpi_dma_controller_register(). Normally this function will not need to
143 * be called and the resource management code will ensure that the resource is
144 * freed.
145 */
146void devm_acpi_dma_controller_free(struct device *dev)
147{
148 WARN_ON(devres_destroy(dev, devm_acpi_dma_release, NULL, NULL));
149}
150EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_free);
151
152struct acpi_dma_parser_data {
153 struct acpi_dma_spec dma_spec;
154 size_t index;
155 size_t n;
156};
157
158/**
159 * acpi_dma_parse_fixed_dma - Parse FixedDMA ACPI resources to a DMA specifier
160 * @res: struct acpi_resource to get FixedDMA resources from
161 * @data: pointer to a helper struct acpi_dma_parser_data
162 */
163static int acpi_dma_parse_fixed_dma(struct acpi_resource *res, void *data)
164{
165 struct acpi_dma_parser_data *pdata = data;
166
167 if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) {
168 struct acpi_resource_fixed_dma *dma = &res->data.fixed_dma;
169
170 if (pdata->n++ == pdata->index) {
171 pdata->dma_spec.chan_id = dma->channels;
172 pdata->dma_spec.slave_id = dma->request_lines;
173 }
174 }
175
176 /* Tell the ACPI core to skip this resource */
177 return 1;
178}
179
180/**
181 * acpi_dma_request_slave_chan_by_index - Get the DMA slave channel
182 * @dev: struct device to get DMA request from
183 * @index: index of FixedDMA descriptor for @dev
184 *
185 * Returns pointer to appropriate dma channel on success or NULL on error.
186 */
187struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,
188 size_t index)
189{
190 struct acpi_dma_parser_data pdata;
191 struct acpi_dma_spec *dma_spec = &pdata.dma_spec;
192 struct list_head resource_list;
193 struct acpi_device *adev;
194 struct acpi_dma *adma;
195 struct dma_chan *chan = NULL;
196
197 /* Check if the device was enumerated by ACPI */
198 if (!dev || !ACPI_HANDLE(dev))
199 return NULL;
200
201 if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev))
202 return NULL;
203
204 memset(&pdata, 0, sizeof(pdata));
205 pdata.index = index;
206
207 /* Initial values for the request line and channel */
208 dma_spec->chan_id = -1;
209 dma_spec->slave_id = -1;
210
211 INIT_LIST_HEAD(&resource_list);
212 acpi_dev_get_resources(adev, &resource_list,
213 acpi_dma_parse_fixed_dma, &pdata);
214 acpi_dev_free_resource_list(&resource_list);
215
216 if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0)
217 return NULL;
218
219 mutex_lock(&acpi_dma_lock);
220
221 list_for_each_entry(adma, &acpi_dma_list, dma_controllers) {
222 dma_spec->dev = adma->dev;
223 chan = adma->acpi_dma_xlate(dma_spec, adma);
224 if (chan)
225 break;
226 }
227
228 mutex_unlock(&acpi_dma_lock);
229 return chan;
230}
231EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index);
232
233/**
234 * acpi_dma_request_slave_chan_by_name - Get the DMA slave channel
235 * @dev: struct device to get DMA request from
236 * @name: represents corresponding FixedDMA descriptor for @dev
237 *
238 * In order to support both Device Tree and ACPI in a single driver we
239 * translate the names "tx" and "rx" here based on the most common case where
240 * the first FixedDMA descriptor is TX and second is RX.
241 *
242 * Returns pointer to appropriate dma channel on success or NULL on error.
243 */
244struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,
245 const char *name)
246{
247 size_t index;
248
249 if (!strcmp(name, "tx"))
250 index = 0;
251 else if (!strcmp(name, "rx"))
252 index = 1;
253 else
254 return NULL;
255
256 return acpi_dma_request_slave_chan_by_index(dev, index);
257}
258EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_name);
259
260/**
261 * acpi_dma_simple_xlate - Simple ACPI DMA engine translation helper
262 * @dma_spec: pointer to ACPI DMA specifier
263 * @adma: pointer to ACPI DMA controller data
264 *
265 * A simple translation function for ACPI based devices. Passes &struct
266 * dma_spec to the DMA controller driver provided filter function. Returns
267 * pointer to the channel if found or %NULL otherwise.
268 */
269struct dma_chan *acpi_dma_simple_xlate(struct acpi_dma_spec *dma_spec,
270 struct acpi_dma *adma)
271{
272 struct acpi_dma_filter_info *info = adma->data;
273
274 if (!info || !info->filter_fn)
275 return NULL;
276
277 return dma_request_channel(info->dma_cap, info->filter_fn, dma_spec);
278}
279EXPORT_SYMBOL_GPL(acpi_dma_simple_xlate);