diff options
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/Kconfig | 34 | ||||
-rw-r--r-- | drivers/dma/Makefile | 3 | ||||
-rw-r--r-- | drivers/dma/dmaengine.c | 408 | ||||
-rw-r--r-- | drivers/dma/ioatdma.c | 840 | ||||
-rw-r--r-- | drivers/dma/ioatdma.h | 125 | ||||
-rw-r--r-- | drivers/dma/ioatdma_hw.h | 52 | ||||
-rw-r--r-- | drivers/dma/ioatdma_io.h | 118 | ||||
-rw-r--r-- | drivers/dma/ioatdma_registers.h | 126 | ||||
-rw-r--r-- | drivers/dma/iovlock.c | 301 |
9 files changed, 2007 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig new file mode 100644 index 00000000000..30d021d1a07 --- /dev/null +++ b/drivers/dma/Kconfig | |||
@@ -0,0 +1,34 @@ | |||
1 | # | ||
2 | # DMA engine configuration | ||
3 | # | ||
4 | |||
5 | menu "DMA Engine support" | ||
6 | |||
7 | config DMA_ENGINE | ||
8 | bool "Support for DMA engines" | ||
9 | ---help--- | ||
10 | DMA engines offload copy operations from the CPU to dedicated | ||
11 | hardware, allowing the copies to happen asynchronously. | ||
12 | |||
13 | comment "DMA Clients" | ||
14 | |||
15 | config NET_DMA | ||
16 | bool "Network: TCP receive copy offload" | ||
17 | depends on DMA_ENGINE && NET | ||
18 | default y | ||
19 | ---help--- | ||
20 | This enables the use of DMA engines in the network stack to | ||
21 | offload receive copy-to-user operations, freeing CPU cycles. | ||
22 | Since this is the main user of the DMA engine, it should be enabled; | ||
23 | say Y here. | ||
24 | |||
25 | comment "DMA Devices" | ||
26 | |||
27 | config INTEL_IOATDMA | ||
28 | tristate "Intel I/OAT DMA support" | ||
29 | depends on DMA_ENGINE && PCI | ||
30 | default m | ||
31 | ---help--- | ||
32 | Enable support for the Intel(R) I/OAT DMA engine. | ||
33 | |||
34 | endmenu | ||
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile new file mode 100644 index 00000000000..bdcfdbdb1ae --- /dev/null +++ b/drivers/dma/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_DMA_ENGINE) += dmaengine.o | ||
2 | obj-$(CONFIG_NET_DMA) += iovlock.o | ||
3 | obj-$(CONFIG_INTEL_IOATDMA) += ioatdma.o | ||
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c new file mode 100644 index 00000000000..5829143558e --- /dev/null +++ b/drivers/dma/dmaengine.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * This code implements the DMA subsystem. It provides a HW-neutral interface | ||
24 | * for other kernel code to use asynchronous memory copy capabilities, | ||
25 | * if present, and allows different HW DMA drivers to register as providing | ||
26 | * this capability. | ||
27 | * | ||
28 | * Due to the fact we are accelerating what is already a relatively fast | ||
29 | * operation, the code goes to great lengths to avoid additional overhead, | ||
30 | * such as locking. | ||
31 | * | ||
32 | * LOCKING: | ||
33 | * | ||
34 | * The subsystem keeps two global lists, dma_device_list and dma_client_list. | ||
35 | * Both of these are protected by a mutex, dma_list_mutex. | ||
36 | * | ||
37 | * Each device has a channels list, which runs unlocked but is never modified | ||
38 | * once the device is registered, it's just setup by the driver. | ||
39 | * | ||
40 | * Each client has a channels list, it's only modified under the client->lock | ||
41 | * and in an RCU callback, so it's safe to read under rcu_read_lock(). | ||
42 | * | ||
43 | * Each device has a kref, which is initialized to 1 when the device is | ||
44 | * registered. A kref_put is done for each class_device registered. When the | ||
45 | * class_device is released, the coresponding kref_put is done in the release | ||
46 | * method. Every time one of the device's channels is allocated to a client, | ||
47 | * a kref_get occurs. When the channel is freed, the coresponding kref_put | ||
48 | * happens. The device's release function does a completion, so | ||
49 | * unregister_device does a remove event, class_device_unregister, a kref_put | ||
50 | * for the first reference, then waits on the completion for all other | ||
51 | * references to finish. | ||
52 | * | ||
53 | * Each channel has an open-coded implementation of Rusty Russell's "bigref," | ||
54 | * with a kref and a per_cpu local_t. A single reference is set when on an | ||
55 | * ADDED event, and removed with a REMOVE event. Net DMA client takes an | ||
56 | * extra reference per outstanding transaction. The relase function does a | ||
57 | * kref_put on the device. -ChrisL | ||
58 | */ | ||
59 | |||
60 | #include <linux/init.h> | ||
61 | #include <linux/module.h> | ||
62 | #include <linux/device.h> | ||
63 | #include <linux/dmaengine.h> | ||
64 | #include <linux/hardirq.h> | ||
65 | #include <linux/spinlock.h> | ||
66 | #include <linux/percpu.h> | ||
67 | #include <linux/rcupdate.h> | ||
68 | #include <linux/mutex.h> | ||
69 | |||
70 | static DEFINE_MUTEX(dma_list_mutex); | ||
71 | static LIST_HEAD(dma_device_list); | ||
72 | static LIST_HEAD(dma_client_list); | ||
73 | |||
74 | /* --- sysfs implementation --- */ | ||
75 | |||
76 | static ssize_t show_memcpy_count(struct class_device *cd, char *buf) | ||
77 | { | ||
78 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); | ||
79 | unsigned long count = 0; | ||
80 | int i; | ||
81 | |||
82 | for_each_possible_cpu(i) | ||
83 | count += per_cpu_ptr(chan->local, i)->memcpy_count; | ||
84 | |||
85 | return sprintf(buf, "%lu\n", count); | ||
86 | } | ||
87 | |||
88 | static ssize_t show_bytes_transferred(struct class_device *cd, char *buf) | ||
89 | { | ||
90 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); | ||
91 | unsigned long count = 0; | ||
92 | int i; | ||
93 | |||
94 | for_each_possible_cpu(i) | ||
95 | count += per_cpu_ptr(chan->local, i)->bytes_transferred; | ||
96 | |||
97 | return sprintf(buf, "%lu\n", count); | ||
98 | } | ||
99 | |||
100 | static ssize_t show_in_use(struct class_device *cd, char *buf) | ||
101 | { | ||
102 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); | ||
103 | |||
104 | return sprintf(buf, "%d\n", (chan->client ? 1 : 0)); | ||
105 | } | ||
106 | |||
107 | static struct class_device_attribute dma_class_attrs[] = { | ||
108 | __ATTR(memcpy_count, S_IRUGO, show_memcpy_count, NULL), | ||
109 | __ATTR(bytes_transferred, S_IRUGO, show_bytes_transferred, NULL), | ||
110 | __ATTR(in_use, S_IRUGO, show_in_use, NULL), | ||
111 | __ATTR_NULL | ||
112 | }; | ||
113 | |||
114 | static void dma_async_device_cleanup(struct kref *kref); | ||
115 | |||
116 | static void dma_class_dev_release(struct class_device *cd) | ||
117 | { | ||
118 | struct dma_chan *chan = container_of(cd, struct dma_chan, class_dev); | ||
119 | kref_put(&chan->device->refcount, dma_async_device_cleanup); | ||
120 | } | ||
121 | |||
122 | static struct class dma_devclass = { | ||
123 | .name = "dma", | ||
124 | .class_dev_attrs = dma_class_attrs, | ||
125 | .release = dma_class_dev_release, | ||
126 | }; | ||
127 | |||
128 | /* --- client and device registration --- */ | ||
129 | |||
130 | /** | ||
131 | * dma_client_chan_alloc - try to allocate a channel to a client | ||
132 | * @client: &dma_client | ||
133 | * | ||
134 | * Called with dma_list_mutex held. | ||
135 | */ | ||
136 | static struct dma_chan *dma_client_chan_alloc(struct dma_client *client) | ||
137 | { | ||
138 | struct dma_device *device; | ||
139 | struct dma_chan *chan; | ||
140 | unsigned long flags; | ||
141 | int desc; /* allocated descriptor count */ | ||
142 | |||
143 | /* Find a channel, any DMA engine will do */ | ||
144 | list_for_each_entry(device, &dma_device_list, global_node) { | ||
145 | list_for_each_entry(chan, &device->channels, device_node) { | ||
146 | if (chan->client) | ||
147 | continue; | ||
148 | |||
149 | desc = chan->device->device_alloc_chan_resources(chan); | ||
150 | if (desc >= 0) { | ||
151 | kref_get(&device->refcount); | ||
152 | kref_init(&chan->refcount); | ||
153 | chan->slow_ref = 0; | ||
154 | INIT_RCU_HEAD(&chan->rcu); | ||
155 | chan->client = client; | ||
156 | spin_lock_irqsave(&client->lock, flags); | ||
157 | list_add_tail_rcu(&chan->client_node, | ||
158 | &client->channels); | ||
159 | spin_unlock_irqrestore(&client->lock, flags); | ||
160 | return chan; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * dma_client_chan_free - release a DMA channel | ||
170 | * @chan: &dma_chan | ||
171 | */ | ||
172 | void dma_chan_cleanup(struct kref *kref) | ||
173 | { | ||
174 | struct dma_chan *chan = container_of(kref, struct dma_chan, refcount); | ||
175 | chan->device->device_free_chan_resources(chan); | ||
176 | chan->client = NULL; | ||
177 | kref_put(&chan->device->refcount, dma_async_device_cleanup); | ||
178 | } | ||
179 | |||
180 | static void dma_chan_free_rcu(struct rcu_head *rcu) | ||
181 | { | ||
182 | struct dma_chan *chan = container_of(rcu, struct dma_chan, rcu); | ||
183 | int bias = 0x7FFFFFFF; | ||
184 | int i; | ||
185 | for_each_possible_cpu(i) | ||
186 | bias -= local_read(&per_cpu_ptr(chan->local, i)->refcount); | ||
187 | atomic_sub(bias, &chan->refcount.refcount); | ||
188 | kref_put(&chan->refcount, dma_chan_cleanup); | ||
189 | } | ||
190 | |||
191 | static void dma_client_chan_free(struct dma_chan *chan) | ||
192 | { | ||
193 | atomic_add(0x7FFFFFFF, &chan->refcount.refcount); | ||
194 | chan->slow_ref = 1; | ||
195 | call_rcu(&chan->rcu, dma_chan_free_rcu); | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * dma_chans_rebalance - reallocate channels to clients | ||
200 | * | ||
201 | * When the number of DMA channel in the system changes, | ||
202 | * channels need to be rebalanced among clients | ||
203 | */ | ||
204 | static void dma_chans_rebalance(void) | ||
205 | { | ||
206 | struct dma_client *client; | ||
207 | struct dma_chan *chan; | ||
208 | unsigned long flags; | ||
209 | |||
210 | mutex_lock(&dma_list_mutex); | ||
211 | |||
212 | list_for_each_entry(client, &dma_client_list, global_node) { | ||
213 | while (client->chans_desired > client->chan_count) { | ||
214 | chan = dma_client_chan_alloc(client); | ||
215 | if (!chan) | ||
216 | break; | ||
217 | client->chan_count++; | ||
218 | client->event_callback(client, | ||
219 | chan, | ||
220 | DMA_RESOURCE_ADDED); | ||
221 | } | ||
222 | while (client->chans_desired < client->chan_count) { | ||
223 | spin_lock_irqsave(&client->lock, flags); | ||
224 | chan = list_entry(client->channels.next, | ||
225 | struct dma_chan, | ||
226 | client_node); | ||
227 | list_del_rcu(&chan->client_node); | ||
228 | spin_unlock_irqrestore(&client->lock, flags); | ||
229 | client->chan_count--; | ||
230 | client->event_callback(client, | ||
231 | chan, | ||
232 | DMA_RESOURCE_REMOVED); | ||
233 | dma_client_chan_free(chan); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | mutex_unlock(&dma_list_mutex); | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * dma_async_client_register - allocate and register a &dma_client | ||
242 | * @event_callback: callback for notification of channel addition/removal | ||
243 | */ | ||
244 | struct dma_client *dma_async_client_register(dma_event_callback event_callback) | ||
245 | { | ||
246 | struct dma_client *client; | ||
247 | |||
248 | client = kzalloc(sizeof(*client), GFP_KERNEL); | ||
249 | if (!client) | ||
250 | return NULL; | ||
251 | |||
252 | INIT_LIST_HEAD(&client->channels); | ||
253 | spin_lock_init(&client->lock); | ||
254 | client->chans_desired = 0; | ||
255 | client->chan_count = 0; | ||
256 | client->event_callback = event_callback; | ||
257 | |||
258 | mutex_lock(&dma_list_mutex); | ||
259 | list_add_tail(&client->global_node, &dma_client_list); | ||
260 | mutex_unlock(&dma_list_mutex); | ||
261 | |||
262 | return client; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * dma_async_client_unregister - unregister a client and free the &dma_client | ||
267 | * @client: | ||
268 | * | ||
269 | * Force frees any allocated DMA channels, frees the &dma_client memory | ||
270 | */ | ||
271 | void dma_async_client_unregister(struct dma_client *client) | ||
272 | { | ||
273 | struct dma_chan *chan; | ||
274 | |||
275 | if (!client) | ||
276 | return; | ||
277 | |||
278 | rcu_read_lock(); | ||
279 | list_for_each_entry_rcu(chan, &client->channels, client_node) | ||
280 | dma_client_chan_free(chan); | ||
281 | rcu_read_unlock(); | ||
282 | |||
283 | mutex_lock(&dma_list_mutex); | ||
284 | list_del(&client->global_node); | ||
285 | mutex_unlock(&dma_list_mutex); | ||
286 | |||
287 | kfree(client); | ||
288 | dma_chans_rebalance(); | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * dma_async_client_chan_request - request DMA channels | ||
293 | * @client: &dma_client | ||
294 | * @number: count of DMA channels requested | ||
295 | * | ||
296 | * Clients call dma_async_client_chan_request() to specify how many | ||
297 | * DMA channels they need, 0 to free all currently allocated. | ||
298 | * The resulting allocations/frees are indicated to the client via the | ||
299 | * event callback. | ||
300 | */ | ||
301 | void dma_async_client_chan_request(struct dma_client *client, | ||
302 | unsigned int number) | ||
303 | { | ||
304 | client->chans_desired = number; | ||
305 | dma_chans_rebalance(); | ||
306 | } | ||
307 | |||
308 | /** | ||
309 | * dma_async_device_register - | ||
310 | * @device: &dma_device | ||
311 | */ | ||
312 | int dma_async_device_register(struct dma_device *device) | ||
313 | { | ||
314 | static int id; | ||
315 | int chancnt = 0; | ||
316 | struct dma_chan* chan; | ||
317 | |||
318 | if (!device) | ||
319 | return -ENODEV; | ||
320 | |||
321 | init_completion(&device->done); | ||
322 | kref_init(&device->refcount); | ||
323 | device->dev_id = id++; | ||
324 | |||
325 | /* represent channels in sysfs. Probably want devs too */ | ||
326 | list_for_each_entry(chan, &device->channels, device_node) { | ||
327 | chan->local = alloc_percpu(typeof(*chan->local)); | ||
328 | if (chan->local == NULL) | ||
329 | continue; | ||
330 | |||
331 | chan->chan_id = chancnt++; | ||
332 | chan->class_dev.class = &dma_devclass; | ||
333 | chan->class_dev.dev = NULL; | ||
334 | snprintf(chan->class_dev.class_id, BUS_ID_SIZE, "dma%dchan%d", | ||
335 | device->dev_id, chan->chan_id); | ||
336 | |||
337 | kref_get(&device->refcount); | ||
338 | class_device_register(&chan->class_dev); | ||
339 | } | ||
340 | |||
341 | mutex_lock(&dma_list_mutex); | ||
342 | list_add_tail(&device->global_node, &dma_device_list); | ||
343 | mutex_unlock(&dma_list_mutex); | ||
344 | |||
345 | dma_chans_rebalance(); | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * dma_async_device_unregister - | ||
352 | * @device: &dma_device | ||
353 | */ | ||
354 | static void dma_async_device_cleanup(struct kref *kref) | ||
355 | { | ||
356 | struct dma_device *device; | ||
357 | |||
358 | device = container_of(kref, struct dma_device, refcount); | ||
359 | complete(&device->done); | ||
360 | } | ||
361 | |||
362 | void dma_async_device_unregister(struct dma_device* device) | ||
363 | { | ||
364 | struct dma_chan *chan; | ||
365 | unsigned long flags; | ||
366 | |||
367 | mutex_lock(&dma_list_mutex); | ||
368 | list_del(&device->global_node); | ||
369 | mutex_unlock(&dma_list_mutex); | ||
370 | |||
371 | list_for_each_entry(chan, &device->channels, device_node) { | ||
372 | if (chan->client) { | ||
373 | spin_lock_irqsave(&chan->client->lock, flags); | ||
374 | list_del(&chan->client_node); | ||
375 | chan->client->chan_count--; | ||
376 | spin_unlock_irqrestore(&chan->client->lock, flags); | ||
377 | chan->client->event_callback(chan->client, | ||
378 | chan, | ||
379 | DMA_RESOURCE_REMOVED); | ||
380 | dma_client_chan_free(chan); | ||
381 | } | ||
382 | class_device_unregister(&chan->class_dev); | ||
383 | } | ||
384 | dma_chans_rebalance(); | ||
385 | |||
386 | kref_put(&device->refcount, dma_async_device_cleanup); | ||
387 | wait_for_completion(&device->done); | ||
388 | } | ||
389 | |||
390 | static int __init dma_bus_init(void) | ||
391 | { | ||
392 | mutex_init(&dma_list_mutex); | ||
393 | return class_register(&dma_devclass); | ||
394 | } | ||
395 | |||
396 | subsys_initcall(dma_bus_init); | ||
397 | |||
398 | EXPORT_SYMBOL(dma_async_client_register); | ||
399 | EXPORT_SYMBOL(dma_async_client_unregister); | ||
400 | EXPORT_SYMBOL(dma_async_client_chan_request); | ||
401 | EXPORT_SYMBOL(dma_async_memcpy_buf_to_buf); | ||
402 | EXPORT_SYMBOL(dma_async_memcpy_buf_to_pg); | ||
403 | EXPORT_SYMBOL(dma_async_memcpy_pg_to_pg); | ||
404 | EXPORT_SYMBOL(dma_async_memcpy_complete); | ||
405 | EXPORT_SYMBOL(dma_async_memcpy_issue_pending); | ||
406 | EXPORT_SYMBOL(dma_async_device_register); | ||
407 | EXPORT_SYMBOL(dma_async_device_unregister); | ||
408 | EXPORT_SYMBOL(dma_chan_cleanup); | ||
diff --git a/drivers/dma/ioatdma.c b/drivers/dma/ioatdma.c new file mode 100644 index 00000000000..0fdf7fbd649 --- /dev/null +++ b/drivers/dma/ioatdma.c | |||
@@ -0,0 +1,840 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * This driver supports an Intel I/OAT DMA engine, which does asynchronous | ||
24 | * copy operations. | ||
25 | */ | ||
26 | |||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/dmaengine.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/dma-mapping.h> | ||
34 | #include "ioatdma.h" | ||
35 | #include "ioatdma_io.h" | ||
36 | #include "ioatdma_registers.h" | ||
37 | #include "ioatdma_hw.h" | ||
38 | |||
39 | #define to_ioat_chan(chan) container_of(chan, struct ioat_dma_chan, common) | ||
40 | #define to_ioat_device(dev) container_of(dev, struct ioat_device, common) | ||
41 | #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) | ||
42 | |||
43 | /* internal functions */ | ||
44 | static int __devinit ioat_probe(struct pci_dev *pdev, const struct pci_device_id *ent); | ||
45 | static void __devexit ioat_remove(struct pci_dev *pdev); | ||
46 | |||
47 | static int enumerate_dma_channels(struct ioat_device *device) | ||
48 | { | ||
49 | u8 xfercap_scale; | ||
50 | u32 xfercap; | ||
51 | int i; | ||
52 | struct ioat_dma_chan *ioat_chan; | ||
53 | |||
54 | device->common.chancnt = ioatdma_read8(device, IOAT_CHANCNT_OFFSET); | ||
55 | xfercap_scale = ioatdma_read8(device, IOAT_XFERCAP_OFFSET); | ||
56 | xfercap = (xfercap_scale == 0 ? -1 : (1UL << xfercap_scale)); | ||
57 | |||
58 | for (i = 0; i < device->common.chancnt; i++) { | ||
59 | ioat_chan = kzalloc(sizeof(*ioat_chan), GFP_KERNEL); | ||
60 | if (!ioat_chan) { | ||
61 | device->common.chancnt = i; | ||
62 | break; | ||
63 | } | ||
64 | |||
65 | ioat_chan->device = device; | ||
66 | ioat_chan->reg_base = device->reg_base + (0x80 * (i + 1)); | ||
67 | ioat_chan->xfercap = xfercap; | ||
68 | spin_lock_init(&ioat_chan->cleanup_lock); | ||
69 | spin_lock_init(&ioat_chan->desc_lock); | ||
70 | INIT_LIST_HEAD(&ioat_chan->free_desc); | ||
71 | INIT_LIST_HEAD(&ioat_chan->used_desc); | ||
72 | /* This should be made common somewhere in dmaengine.c */ | ||
73 | ioat_chan->common.device = &device->common; | ||
74 | ioat_chan->common.client = NULL; | ||
75 | list_add_tail(&ioat_chan->common.device_node, | ||
76 | &device->common.channels); | ||
77 | } | ||
78 | return device->common.chancnt; | ||
79 | } | ||
80 | |||
81 | static struct ioat_desc_sw *ioat_dma_alloc_descriptor( | ||
82 | struct ioat_dma_chan *ioat_chan, | ||
83 | int flags) | ||
84 | { | ||
85 | struct ioat_dma_descriptor *desc; | ||
86 | struct ioat_desc_sw *desc_sw; | ||
87 | struct ioat_device *ioat_device; | ||
88 | dma_addr_t phys; | ||
89 | |||
90 | ioat_device = to_ioat_device(ioat_chan->common.device); | ||
91 | desc = pci_pool_alloc(ioat_device->dma_pool, flags, &phys); | ||
92 | if (unlikely(!desc)) | ||
93 | return NULL; | ||
94 | |||
95 | desc_sw = kzalloc(sizeof(*desc_sw), flags); | ||
96 | if (unlikely(!desc_sw)) { | ||
97 | pci_pool_free(ioat_device->dma_pool, desc, phys); | ||
98 | return NULL; | ||
99 | } | ||
100 | |||
101 | memset(desc, 0, sizeof(*desc)); | ||
102 | desc_sw->hw = desc; | ||
103 | desc_sw->phys = phys; | ||
104 | |||
105 | return desc_sw; | ||
106 | } | ||
107 | |||
108 | #define INITIAL_IOAT_DESC_COUNT 128 | ||
109 | |||
110 | static void ioat_start_null_desc(struct ioat_dma_chan *ioat_chan); | ||
111 | |||
112 | /* returns the actual number of allocated descriptors */ | ||
113 | static int ioat_dma_alloc_chan_resources(struct dma_chan *chan) | ||
114 | { | ||
115 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
116 | struct ioat_desc_sw *desc = NULL; | ||
117 | u16 chanctrl; | ||
118 | u32 chanerr; | ||
119 | int i; | ||
120 | LIST_HEAD(tmp_list); | ||
121 | |||
122 | /* | ||
123 | * In-use bit automatically set by reading chanctrl | ||
124 | * If 0, we got it, if 1, someone else did | ||
125 | */ | ||
126 | chanctrl = ioatdma_chan_read16(ioat_chan, IOAT_CHANCTRL_OFFSET); | ||
127 | if (chanctrl & IOAT_CHANCTRL_CHANNEL_IN_USE) | ||
128 | return -EBUSY; | ||
129 | |||
130 | /* Setup register to interrupt and write completion status on error */ | ||
131 | chanctrl = IOAT_CHANCTRL_CHANNEL_IN_USE | | ||
132 | IOAT_CHANCTRL_ERR_INT_EN | | ||
133 | IOAT_CHANCTRL_ANY_ERR_ABORT_EN | | ||
134 | IOAT_CHANCTRL_ERR_COMPLETION_EN; | ||
135 | ioatdma_chan_write16(ioat_chan, IOAT_CHANCTRL_OFFSET, chanctrl); | ||
136 | |||
137 | chanerr = ioatdma_chan_read32(ioat_chan, IOAT_CHANERR_OFFSET); | ||
138 | if (chanerr) { | ||
139 | printk("IOAT: CHANERR = %x, clearing\n", chanerr); | ||
140 | ioatdma_chan_write32(ioat_chan, IOAT_CHANERR_OFFSET, chanerr); | ||
141 | } | ||
142 | |||
143 | /* Allocate descriptors */ | ||
144 | for (i = 0; i < INITIAL_IOAT_DESC_COUNT; i++) { | ||
145 | desc = ioat_dma_alloc_descriptor(ioat_chan, GFP_KERNEL); | ||
146 | if (!desc) { | ||
147 | printk(KERN_ERR "IOAT: Only %d initial descriptors\n", i); | ||
148 | break; | ||
149 | } | ||
150 | list_add_tail(&desc->node, &tmp_list); | ||
151 | } | ||
152 | spin_lock_bh(&ioat_chan->desc_lock); | ||
153 | list_splice(&tmp_list, &ioat_chan->free_desc); | ||
154 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
155 | |||
156 | /* allocate a completion writeback area */ | ||
157 | /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ | ||
158 | ioat_chan->completion_virt = | ||
159 | pci_pool_alloc(ioat_chan->device->completion_pool, | ||
160 | GFP_KERNEL, | ||
161 | &ioat_chan->completion_addr); | ||
162 | memset(ioat_chan->completion_virt, 0, | ||
163 | sizeof(*ioat_chan->completion_virt)); | ||
164 | ioatdma_chan_write32(ioat_chan, IOAT_CHANCMP_OFFSET_LOW, | ||
165 | ((u64) ioat_chan->completion_addr) & 0x00000000FFFFFFFF); | ||
166 | ioatdma_chan_write32(ioat_chan, IOAT_CHANCMP_OFFSET_HIGH, | ||
167 | ((u64) ioat_chan->completion_addr) >> 32); | ||
168 | |||
169 | ioat_start_null_desc(ioat_chan); | ||
170 | return i; | ||
171 | } | ||
172 | |||
173 | static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *ioat_chan); | ||
174 | |||
175 | static void ioat_dma_free_chan_resources(struct dma_chan *chan) | ||
176 | { | ||
177 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
178 | struct ioat_device *ioat_device = to_ioat_device(chan->device); | ||
179 | struct ioat_desc_sw *desc, *_desc; | ||
180 | u16 chanctrl; | ||
181 | int in_use_descs = 0; | ||
182 | |||
183 | ioat_dma_memcpy_cleanup(ioat_chan); | ||
184 | |||
185 | ioatdma_chan_write8(ioat_chan, IOAT_CHANCMD_OFFSET, IOAT_CHANCMD_RESET); | ||
186 | |||
187 | spin_lock_bh(&ioat_chan->desc_lock); | ||
188 | list_for_each_entry_safe(desc, _desc, &ioat_chan->used_desc, node) { | ||
189 | in_use_descs++; | ||
190 | list_del(&desc->node); | ||
191 | pci_pool_free(ioat_device->dma_pool, desc->hw, desc->phys); | ||
192 | kfree(desc); | ||
193 | } | ||
194 | list_for_each_entry_safe(desc, _desc, &ioat_chan->free_desc, node) { | ||
195 | list_del(&desc->node); | ||
196 | pci_pool_free(ioat_device->dma_pool, desc->hw, desc->phys); | ||
197 | kfree(desc); | ||
198 | } | ||
199 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
200 | |||
201 | pci_pool_free(ioat_device->completion_pool, | ||
202 | ioat_chan->completion_virt, | ||
203 | ioat_chan->completion_addr); | ||
204 | |||
205 | /* one is ok since we left it on there on purpose */ | ||
206 | if (in_use_descs > 1) | ||
207 | printk(KERN_ERR "IOAT: Freeing %d in use descriptors!\n", | ||
208 | in_use_descs - 1); | ||
209 | |||
210 | ioat_chan->last_completion = ioat_chan->completion_addr = 0; | ||
211 | |||
212 | /* Tell hw the chan is free */ | ||
213 | chanctrl = ioatdma_chan_read16(ioat_chan, IOAT_CHANCTRL_OFFSET); | ||
214 | chanctrl &= ~IOAT_CHANCTRL_CHANNEL_IN_USE; | ||
215 | ioatdma_chan_write16(ioat_chan, IOAT_CHANCTRL_OFFSET, chanctrl); | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * do_ioat_dma_memcpy - actual function that initiates a IOAT DMA transaction | ||
220 | * @chan: IOAT DMA channel handle | ||
221 | * @dest: DMA destination address | ||
222 | * @src: DMA source address | ||
223 | * @len: transaction length in bytes | ||
224 | */ | ||
225 | |||
226 | static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan, | ||
227 | dma_addr_t dest, | ||
228 | dma_addr_t src, | ||
229 | size_t len) | ||
230 | { | ||
231 | struct ioat_desc_sw *first; | ||
232 | struct ioat_desc_sw *prev; | ||
233 | struct ioat_desc_sw *new; | ||
234 | dma_cookie_t cookie; | ||
235 | LIST_HEAD(new_chain); | ||
236 | u32 copy; | ||
237 | size_t orig_len; | ||
238 | dma_addr_t orig_src, orig_dst; | ||
239 | unsigned int desc_count = 0; | ||
240 | unsigned int append = 0; | ||
241 | |||
242 | if (!ioat_chan || !dest || !src) | ||
243 | return -EFAULT; | ||
244 | |||
245 | if (!len) | ||
246 | return ioat_chan->common.cookie; | ||
247 | |||
248 | orig_len = len; | ||
249 | orig_src = src; | ||
250 | orig_dst = dest; | ||
251 | |||
252 | first = NULL; | ||
253 | prev = NULL; | ||
254 | |||
255 | spin_lock_bh(&ioat_chan->desc_lock); | ||
256 | |||
257 | while (len) { | ||
258 | if (!list_empty(&ioat_chan->free_desc)) { | ||
259 | new = to_ioat_desc(ioat_chan->free_desc.next); | ||
260 | list_del(&new->node); | ||
261 | } else { | ||
262 | /* try to get another desc */ | ||
263 | new = ioat_dma_alloc_descriptor(ioat_chan, GFP_ATOMIC); | ||
264 | /* will this ever happen? */ | ||
265 | /* TODO add upper limit on these */ | ||
266 | BUG_ON(!new); | ||
267 | } | ||
268 | |||
269 | copy = min((u32) len, ioat_chan->xfercap); | ||
270 | |||
271 | new->hw->size = copy; | ||
272 | new->hw->ctl = 0; | ||
273 | new->hw->src_addr = src; | ||
274 | new->hw->dst_addr = dest; | ||
275 | new->cookie = 0; | ||
276 | |||
277 | /* chain together the physical address list for the HW */ | ||
278 | if (!first) | ||
279 | first = new; | ||
280 | else | ||
281 | prev->hw->next = (u64) new->phys; | ||
282 | |||
283 | prev = new; | ||
284 | |||
285 | len -= copy; | ||
286 | dest += copy; | ||
287 | src += copy; | ||
288 | |||
289 | list_add_tail(&new->node, &new_chain); | ||
290 | desc_count++; | ||
291 | } | ||
292 | new->hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; | ||
293 | new->hw->next = 0; | ||
294 | |||
295 | /* cookie incr and addition to used_list must be atomic */ | ||
296 | |||
297 | cookie = ioat_chan->common.cookie; | ||
298 | cookie++; | ||
299 | if (cookie < 0) | ||
300 | cookie = 1; | ||
301 | ioat_chan->common.cookie = new->cookie = cookie; | ||
302 | |||
303 | pci_unmap_addr_set(new, src, orig_src); | ||
304 | pci_unmap_addr_set(new, dst, orig_dst); | ||
305 | pci_unmap_len_set(new, src_len, orig_len); | ||
306 | pci_unmap_len_set(new, dst_len, orig_len); | ||
307 | |||
308 | /* write address into NextDescriptor field of last desc in chain */ | ||
309 | to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = first->phys; | ||
310 | list_splice_init(&new_chain, ioat_chan->used_desc.prev); | ||
311 | |||
312 | ioat_chan->pending += desc_count; | ||
313 | if (ioat_chan->pending >= 20) { | ||
314 | append = 1; | ||
315 | ioat_chan->pending = 0; | ||
316 | } | ||
317 | |||
318 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
319 | |||
320 | if (append) | ||
321 | ioatdma_chan_write8(ioat_chan, | ||
322 | IOAT_CHANCMD_OFFSET, | ||
323 | IOAT_CHANCMD_APPEND); | ||
324 | return cookie; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * ioat_dma_memcpy_buf_to_buf - wrapper that takes src & dest bufs | ||
329 | * @chan: IOAT DMA channel handle | ||
330 | * @dest: DMA destination address | ||
331 | * @src: DMA source address | ||
332 | * @len: transaction length in bytes | ||
333 | */ | ||
334 | |||
335 | static dma_cookie_t ioat_dma_memcpy_buf_to_buf(struct dma_chan *chan, | ||
336 | void *dest, | ||
337 | void *src, | ||
338 | size_t len) | ||
339 | { | ||
340 | dma_addr_t dest_addr; | ||
341 | dma_addr_t src_addr; | ||
342 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
343 | |||
344 | dest_addr = pci_map_single(ioat_chan->device->pdev, | ||
345 | dest, len, PCI_DMA_FROMDEVICE); | ||
346 | src_addr = pci_map_single(ioat_chan->device->pdev, | ||
347 | src, len, PCI_DMA_TODEVICE); | ||
348 | |||
349 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * ioat_dma_memcpy_buf_to_pg - wrapper, copying from a buf to a page | ||
354 | * @chan: IOAT DMA channel handle | ||
355 | * @page: pointer to the page to copy to | ||
356 | * @offset: offset into that page | ||
357 | * @src: DMA source address | ||
358 | * @len: transaction length in bytes | ||
359 | */ | ||
360 | |||
361 | static dma_cookie_t ioat_dma_memcpy_buf_to_pg(struct dma_chan *chan, | ||
362 | struct page *page, | ||
363 | unsigned int offset, | ||
364 | void *src, | ||
365 | size_t len) | ||
366 | { | ||
367 | dma_addr_t dest_addr; | ||
368 | dma_addr_t src_addr; | ||
369 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
370 | |||
371 | dest_addr = pci_map_page(ioat_chan->device->pdev, | ||
372 | page, offset, len, PCI_DMA_FROMDEVICE); | ||
373 | src_addr = pci_map_single(ioat_chan->device->pdev, | ||
374 | src, len, PCI_DMA_TODEVICE); | ||
375 | |||
376 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * ioat_dma_memcpy_pg_to_pg - wrapper, copying between two pages | ||
381 | * @chan: IOAT DMA channel handle | ||
382 | * @dest_pg: pointer to the page to copy to | ||
383 | * @dest_off: offset into that page | ||
384 | * @src_pg: pointer to the page to copy from | ||
385 | * @src_off: offset into that page | ||
386 | * @len: transaction length in bytes. This is guaranteed to not make a copy | ||
387 | * across a page boundary. | ||
388 | */ | ||
389 | |||
390 | static dma_cookie_t ioat_dma_memcpy_pg_to_pg(struct dma_chan *chan, | ||
391 | struct page *dest_pg, | ||
392 | unsigned int dest_off, | ||
393 | struct page *src_pg, | ||
394 | unsigned int src_off, | ||
395 | size_t len) | ||
396 | { | ||
397 | dma_addr_t dest_addr; | ||
398 | dma_addr_t src_addr; | ||
399 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
400 | |||
401 | dest_addr = pci_map_page(ioat_chan->device->pdev, | ||
402 | dest_pg, dest_off, len, PCI_DMA_FROMDEVICE); | ||
403 | src_addr = pci_map_page(ioat_chan->device->pdev, | ||
404 | src_pg, src_off, len, PCI_DMA_TODEVICE); | ||
405 | |||
406 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * ioat_dma_memcpy_issue_pending - push potentially unrecognoized appended descriptors to hw | ||
411 | * @chan: DMA channel handle | ||
412 | */ | ||
413 | |||
414 | static void ioat_dma_memcpy_issue_pending(struct dma_chan *chan) | ||
415 | { | ||
416 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
417 | |||
418 | if (ioat_chan->pending != 0) { | ||
419 | ioat_chan->pending = 0; | ||
420 | ioatdma_chan_write8(ioat_chan, | ||
421 | IOAT_CHANCMD_OFFSET, | ||
422 | IOAT_CHANCMD_APPEND); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *chan) | ||
427 | { | ||
428 | unsigned long phys_complete; | ||
429 | struct ioat_desc_sw *desc, *_desc; | ||
430 | dma_cookie_t cookie = 0; | ||
431 | |||
432 | prefetch(chan->completion_virt); | ||
433 | |||
434 | if (!spin_trylock(&chan->cleanup_lock)) | ||
435 | return; | ||
436 | |||
437 | /* The completion writeback can happen at any time, | ||
438 | so reads by the driver need to be atomic operations | ||
439 | The descriptor physical addresses are limited to 32-bits | ||
440 | when the CPU can only do a 32-bit mov */ | ||
441 | |||
442 | #if (BITS_PER_LONG == 64) | ||
443 | phys_complete = | ||
444 | chan->completion_virt->full & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR; | ||
445 | #else | ||
446 | phys_complete = chan->completion_virt->low & IOAT_LOW_COMPLETION_MASK; | ||
447 | #endif | ||
448 | |||
449 | if ((chan->completion_virt->full & IOAT_CHANSTS_DMA_TRANSFER_STATUS) == | ||
450 | IOAT_CHANSTS_DMA_TRANSFER_STATUS_HALTED) { | ||
451 | printk("IOAT: Channel halted, chanerr = %x\n", | ||
452 | ioatdma_chan_read32(chan, IOAT_CHANERR_OFFSET)); | ||
453 | |||
454 | /* TODO do something to salvage the situation */ | ||
455 | } | ||
456 | |||
457 | if (phys_complete == chan->last_completion) { | ||
458 | spin_unlock(&chan->cleanup_lock); | ||
459 | return; | ||
460 | } | ||
461 | |||
462 | spin_lock_bh(&chan->desc_lock); | ||
463 | list_for_each_entry_safe(desc, _desc, &chan->used_desc, node) { | ||
464 | |||
465 | /* | ||
466 | * Incoming DMA requests may use multiple descriptors, due to | ||
467 | * exceeding xfercap, perhaps. If so, only the last one will | ||
468 | * have a cookie, and require unmapping. | ||
469 | */ | ||
470 | if (desc->cookie) { | ||
471 | cookie = desc->cookie; | ||
472 | |||
473 | /* yes we are unmapping both _page and _single alloc'd | ||
474 | regions with unmap_page. Is this *really* that bad? | ||
475 | */ | ||
476 | pci_unmap_page(chan->device->pdev, | ||
477 | pci_unmap_addr(desc, dst), | ||
478 | pci_unmap_len(desc, dst_len), | ||
479 | PCI_DMA_FROMDEVICE); | ||
480 | pci_unmap_page(chan->device->pdev, | ||
481 | pci_unmap_addr(desc, src), | ||
482 | pci_unmap_len(desc, src_len), | ||
483 | PCI_DMA_TODEVICE); | ||
484 | } | ||
485 | |||
486 | if (desc->phys != phys_complete) { | ||
487 | /* a completed entry, but not the last, so cleanup */ | ||
488 | list_del(&desc->node); | ||
489 | list_add_tail(&desc->node, &chan->free_desc); | ||
490 | } else { | ||
491 | /* last used desc. Do not remove, so we can append from | ||
492 | it, but don't look at it next time, either */ | ||
493 | desc->cookie = 0; | ||
494 | |||
495 | /* TODO check status bits? */ | ||
496 | break; | ||
497 | } | ||
498 | } | ||
499 | |||
500 | spin_unlock_bh(&chan->desc_lock); | ||
501 | |||
502 | chan->last_completion = phys_complete; | ||
503 | if (cookie != 0) | ||
504 | chan->completed_cookie = cookie; | ||
505 | |||
506 | spin_unlock(&chan->cleanup_lock); | ||
507 | } | ||
508 | |||
509 | /** | ||
510 | * ioat_dma_is_complete - poll the status of a IOAT DMA transaction | ||
511 | * @chan: IOAT DMA channel handle | ||
512 | * @cookie: DMA transaction identifier | ||
513 | */ | ||
514 | |||
515 | static enum dma_status ioat_dma_is_complete(struct dma_chan *chan, | ||
516 | dma_cookie_t cookie, | ||
517 | dma_cookie_t *done, | ||
518 | dma_cookie_t *used) | ||
519 | { | ||
520 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
521 | dma_cookie_t last_used; | ||
522 | dma_cookie_t last_complete; | ||
523 | enum dma_status ret; | ||
524 | |||
525 | last_used = chan->cookie; | ||
526 | last_complete = ioat_chan->completed_cookie; | ||
527 | |||
528 | if (done) | ||
529 | *done= last_complete; | ||
530 | if (used) | ||
531 | *used = last_used; | ||
532 | |||
533 | ret = dma_async_is_complete(cookie, last_complete, last_used); | ||
534 | if (ret == DMA_SUCCESS) | ||
535 | return ret; | ||
536 | |||
537 | ioat_dma_memcpy_cleanup(ioat_chan); | ||
538 | |||
539 | last_used = chan->cookie; | ||
540 | last_complete = ioat_chan->completed_cookie; | ||
541 | |||
542 | if (done) | ||
543 | *done= last_complete; | ||
544 | if (used) | ||
545 | *used = last_used; | ||
546 | |||
547 | return dma_async_is_complete(cookie, last_complete, last_used); | ||
548 | } | ||
549 | |||
550 | /* PCI API */ | ||
551 | |||
552 | static struct pci_device_id ioat_pci_tbl[] = { | ||
553 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, | ||
554 | { 0, } | ||
555 | }; | ||
556 | |||
557 | static struct pci_driver ioat_pci_drv = { | ||
558 | .name = "ioatdma", | ||
559 | .id_table = ioat_pci_tbl, | ||
560 | .probe = ioat_probe, | ||
561 | .remove = __devexit_p(ioat_remove), | ||
562 | }; | ||
563 | |||
564 | static irqreturn_t ioat_do_interrupt(int irq, void *data, struct pt_regs *regs) | ||
565 | { | ||
566 | struct ioat_device *instance = data; | ||
567 | unsigned long attnstatus; | ||
568 | u8 intrctrl; | ||
569 | |||
570 | intrctrl = ioatdma_read8(instance, IOAT_INTRCTRL_OFFSET); | ||
571 | |||
572 | if (!(intrctrl & IOAT_INTRCTRL_MASTER_INT_EN)) | ||
573 | return IRQ_NONE; | ||
574 | |||
575 | if (!(intrctrl & IOAT_INTRCTRL_INT_STATUS)) { | ||
576 | ioatdma_write8(instance, IOAT_INTRCTRL_OFFSET, intrctrl); | ||
577 | return IRQ_NONE; | ||
578 | } | ||
579 | |||
580 | attnstatus = ioatdma_read32(instance, IOAT_ATTNSTATUS_OFFSET); | ||
581 | |||
582 | printk(KERN_ERR "ioatdma error: interrupt! status %lx\n", attnstatus); | ||
583 | |||
584 | ioatdma_write8(instance, IOAT_INTRCTRL_OFFSET, intrctrl); | ||
585 | return IRQ_HANDLED; | ||
586 | } | ||
587 | |||
588 | static void ioat_start_null_desc(struct ioat_dma_chan *ioat_chan) | ||
589 | { | ||
590 | struct ioat_desc_sw *desc; | ||
591 | |||
592 | spin_lock_bh(&ioat_chan->desc_lock); | ||
593 | |||
594 | if (!list_empty(&ioat_chan->free_desc)) { | ||
595 | desc = to_ioat_desc(ioat_chan->free_desc.next); | ||
596 | list_del(&desc->node); | ||
597 | } else { | ||
598 | /* try to get another desc */ | ||
599 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
600 | desc = ioat_dma_alloc_descriptor(ioat_chan, GFP_KERNEL); | ||
601 | spin_lock_bh(&ioat_chan->desc_lock); | ||
602 | /* will this ever happen? */ | ||
603 | BUG_ON(!desc); | ||
604 | } | ||
605 | |||
606 | desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; | ||
607 | desc->hw->next = 0; | ||
608 | |||
609 | list_add_tail(&desc->node, &ioat_chan->used_desc); | ||
610 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
611 | |||
612 | #if (BITS_PER_LONG == 64) | ||
613 | ioatdma_chan_write64(ioat_chan, IOAT_CHAINADDR_OFFSET, desc->phys); | ||
614 | #else | ||
615 | ioatdma_chan_write32(ioat_chan, | ||
616 | IOAT_CHAINADDR_OFFSET_LOW, | ||
617 | (u32) desc->phys); | ||
618 | ioatdma_chan_write32(ioat_chan, IOAT_CHAINADDR_OFFSET_HIGH, 0); | ||
619 | #endif | ||
620 | ioatdma_chan_write8(ioat_chan, IOAT_CHANCMD_OFFSET, IOAT_CHANCMD_START); | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * Perform a IOAT transaction to verify the HW works. | ||
625 | */ | ||
626 | #define IOAT_TEST_SIZE 2000 | ||
627 | |||
628 | static int ioat_self_test(struct ioat_device *device) | ||
629 | { | ||
630 | int i; | ||
631 | u8 *src; | ||
632 | u8 *dest; | ||
633 | struct dma_chan *dma_chan; | ||
634 | dma_cookie_t cookie; | ||
635 | int err = 0; | ||
636 | |||
637 | src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, SLAB_KERNEL); | ||
638 | if (!src) | ||
639 | return -ENOMEM; | ||
640 | dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, SLAB_KERNEL); | ||
641 | if (!dest) { | ||
642 | kfree(src); | ||
643 | return -ENOMEM; | ||
644 | } | ||
645 | |||
646 | /* Fill in src buffer */ | ||
647 | for (i = 0; i < IOAT_TEST_SIZE; i++) | ||
648 | src[i] = (u8)i; | ||
649 | |||
650 | /* Start copy, using first DMA channel */ | ||
651 | dma_chan = container_of(device->common.channels.next, | ||
652 | struct dma_chan, | ||
653 | device_node); | ||
654 | if (ioat_dma_alloc_chan_resources(dma_chan) < 1) { | ||
655 | err = -ENODEV; | ||
656 | goto out; | ||
657 | } | ||
658 | |||
659 | cookie = ioat_dma_memcpy_buf_to_buf(dma_chan, dest, src, IOAT_TEST_SIZE); | ||
660 | ioat_dma_memcpy_issue_pending(dma_chan); | ||
661 | msleep(1); | ||
662 | |||
663 | if (ioat_dma_is_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { | ||
664 | printk(KERN_ERR "ioatdma: Self-test copy timed out, disabling\n"); | ||
665 | err = -ENODEV; | ||
666 | goto free_resources; | ||
667 | } | ||
668 | if (memcmp(src, dest, IOAT_TEST_SIZE)) { | ||
669 | printk(KERN_ERR "ioatdma: Self-test copy failed compare, disabling\n"); | ||
670 | err = -ENODEV; | ||
671 | goto free_resources; | ||
672 | } | ||
673 | |||
674 | free_resources: | ||
675 | ioat_dma_free_chan_resources(dma_chan); | ||
676 | out: | ||
677 | kfree(src); | ||
678 | kfree(dest); | ||
679 | return err; | ||
680 | } | ||
681 | |||
682 | static int __devinit ioat_probe(struct pci_dev *pdev, | ||
683 | const struct pci_device_id *ent) | ||
684 | { | ||
685 | int err; | ||
686 | unsigned long mmio_start, mmio_len; | ||
687 | void *reg_base; | ||
688 | struct ioat_device *device; | ||
689 | |||
690 | err = pci_enable_device(pdev); | ||
691 | if (err) | ||
692 | goto err_enable_device; | ||
693 | |||
694 | err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); | ||
695 | if (err) | ||
696 | err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); | ||
697 | if (err) | ||
698 | goto err_set_dma_mask; | ||
699 | |||
700 | err = pci_request_regions(pdev, ioat_pci_drv.name); | ||
701 | if (err) | ||
702 | goto err_request_regions; | ||
703 | |||
704 | mmio_start = pci_resource_start(pdev, 0); | ||
705 | mmio_len = pci_resource_len(pdev, 0); | ||
706 | |||
707 | reg_base = ioremap(mmio_start, mmio_len); | ||
708 | if (!reg_base) { | ||
709 | err = -ENOMEM; | ||
710 | goto err_ioremap; | ||
711 | } | ||
712 | |||
713 | device = kzalloc(sizeof(*device), GFP_KERNEL); | ||
714 | if (!device) { | ||
715 | err = -ENOMEM; | ||
716 | goto err_kzalloc; | ||
717 | } | ||
718 | |||
719 | /* DMA coherent memory pool for DMA descriptor allocations */ | ||
720 | device->dma_pool = pci_pool_create("dma_desc_pool", pdev, | ||
721 | sizeof(struct ioat_dma_descriptor), 64, 0); | ||
722 | if (!device->dma_pool) { | ||
723 | err = -ENOMEM; | ||
724 | goto err_dma_pool; | ||
725 | } | ||
726 | |||
727 | device->completion_pool = pci_pool_create("completion_pool", pdev, sizeof(u64), SMP_CACHE_BYTES, SMP_CACHE_BYTES); | ||
728 | if (!device->completion_pool) { | ||
729 | err = -ENOMEM; | ||
730 | goto err_completion_pool; | ||
731 | } | ||
732 | |||
733 | device->pdev = pdev; | ||
734 | pci_set_drvdata(pdev, device); | ||
735 | #ifdef CONFIG_PCI_MSI | ||
736 | if (pci_enable_msi(pdev) == 0) { | ||
737 | device->msi = 1; | ||
738 | } else { | ||
739 | device->msi = 0; | ||
740 | } | ||
741 | #endif | ||
742 | err = request_irq(pdev->irq, &ioat_do_interrupt, SA_SHIRQ, "ioat", | ||
743 | device); | ||
744 | if (err) | ||
745 | goto err_irq; | ||
746 | |||
747 | device->reg_base = reg_base; | ||
748 | |||
749 | ioatdma_write8(device, IOAT_INTRCTRL_OFFSET, IOAT_INTRCTRL_MASTER_INT_EN); | ||
750 | pci_set_master(pdev); | ||
751 | |||
752 | INIT_LIST_HEAD(&device->common.channels); | ||
753 | enumerate_dma_channels(device); | ||
754 | |||
755 | device->common.device_alloc_chan_resources = ioat_dma_alloc_chan_resources; | ||
756 | device->common.device_free_chan_resources = ioat_dma_free_chan_resources; | ||
757 | device->common.device_memcpy_buf_to_buf = ioat_dma_memcpy_buf_to_buf; | ||
758 | device->common.device_memcpy_buf_to_pg = ioat_dma_memcpy_buf_to_pg; | ||
759 | device->common.device_memcpy_pg_to_pg = ioat_dma_memcpy_pg_to_pg; | ||
760 | device->common.device_memcpy_complete = ioat_dma_is_complete; | ||
761 | device->common.device_memcpy_issue_pending = ioat_dma_memcpy_issue_pending; | ||
762 | printk(KERN_INFO "Intel(R) I/OAT DMA Engine found, %d channels\n", | ||
763 | device->common.chancnt); | ||
764 | |||
765 | err = ioat_self_test(device); | ||
766 | if (err) | ||
767 | goto err_self_test; | ||
768 | |||
769 | dma_async_device_register(&device->common); | ||
770 | |||
771 | return 0; | ||
772 | |||
773 | err_self_test: | ||
774 | err_irq: | ||
775 | pci_pool_destroy(device->completion_pool); | ||
776 | err_completion_pool: | ||
777 | pci_pool_destroy(device->dma_pool); | ||
778 | err_dma_pool: | ||
779 | kfree(device); | ||
780 | err_kzalloc: | ||
781 | iounmap(reg_base); | ||
782 | err_ioremap: | ||
783 | pci_release_regions(pdev); | ||
784 | err_request_regions: | ||
785 | err_set_dma_mask: | ||
786 | pci_disable_device(pdev); | ||
787 | err_enable_device: | ||
788 | return err; | ||
789 | } | ||
790 | |||
791 | static void __devexit ioat_remove(struct pci_dev *pdev) | ||
792 | { | ||
793 | struct ioat_device *device; | ||
794 | struct dma_chan *chan, *_chan; | ||
795 | struct ioat_dma_chan *ioat_chan; | ||
796 | |||
797 | device = pci_get_drvdata(pdev); | ||
798 | dma_async_device_unregister(&device->common); | ||
799 | |||
800 | free_irq(device->pdev->irq, device); | ||
801 | #ifdef CONFIG_PCI_MSI | ||
802 | if (device->msi) | ||
803 | pci_disable_msi(device->pdev); | ||
804 | #endif | ||
805 | pci_pool_destroy(device->dma_pool); | ||
806 | pci_pool_destroy(device->completion_pool); | ||
807 | iounmap(device->reg_base); | ||
808 | pci_release_regions(pdev); | ||
809 | pci_disable_device(pdev); | ||
810 | list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { | ||
811 | ioat_chan = to_ioat_chan(chan); | ||
812 | list_del(&chan->device_node); | ||
813 | kfree(ioat_chan); | ||
814 | } | ||
815 | kfree(device); | ||
816 | } | ||
817 | |||
818 | /* MODULE API */ | ||
819 | MODULE_VERSION("1.7"); | ||
820 | MODULE_LICENSE("GPL"); | ||
821 | MODULE_AUTHOR("Intel Corporation"); | ||
822 | |||
823 | static int __init ioat_init_module(void) | ||
824 | { | ||
825 | /* it's currently unsafe to unload this module */ | ||
826 | /* if forced, worst case is that rmmod hangs */ | ||
827 | if (THIS_MODULE != NULL) | ||
828 | THIS_MODULE->unsafe = 1; | ||
829 | |||
830 | return pci_module_init(&ioat_pci_drv); | ||
831 | } | ||
832 | |||
833 | module_init(ioat_init_module); | ||
834 | |||
835 | static void __exit ioat_exit_module(void) | ||
836 | { | ||
837 | pci_unregister_driver(&ioat_pci_drv); | ||
838 | } | ||
839 | |||
840 | module_exit(ioat_exit_module); | ||
diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h new file mode 100644 index 00000000000..a5d3b364416 --- /dev/null +++ b/drivers/dma/ioatdma.h | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | #ifndef IOATDMA_H | ||
22 | #define IOATDMA_H | ||
23 | |||
24 | #include <linux/dmaengine.h> | ||
25 | #include "ioatdma_hw.h" | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/dmapool.h> | ||
28 | #include <linux/cache.h> | ||
29 | #include <linux/pci_ids.h> | ||
30 | |||
31 | #define IOAT_LOW_COMPLETION_MASK 0xffffffc0 | ||
32 | |||
33 | extern struct list_head dma_device_list; | ||
34 | extern struct list_head dma_client_list; | ||
35 | |||
36 | /** | ||
37 | * struct ioat_device - internal representation of a IOAT device | ||
38 | * @pdev: PCI-Express device | ||
39 | * @reg_base: MMIO register space base address | ||
40 | * @dma_pool: for allocating DMA descriptors | ||
41 | * @common: embedded struct dma_device | ||
42 | * @msi: Message Signaled Interrupt number | ||
43 | */ | ||
44 | |||
45 | struct ioat_device { | ||
46 | struct pci_dev *pdev; | ||
47 | void *reg_base; | ||
48 | struct pci_pool *dma_pool; | ||
49 | struct pci_pool *completion_pool; | ||
50 | |||
51 | struct dma_device common; | ||
52 | u8 msi; | ||
53 | }; | ||
54 | |||
55 | /** | ||
56 | * struct ioat_dma_chan - internal representation of a DMA channel | ||
57 | * @device: | ||
58 | * @reg_base: | ||
59 | * @sw_in_use: | ||
60 | * @completion: | ||
61 | * @completion_low: | ||
62 | * @completion_high: | ||
63 | * @completed_cookie: last cookie seen completed on cleanup | ||
64 | * @cookie: value of last cookie given to client | ||
65 | * @last_completion: | ||
66 | * @xfercap: | ||
67 | * @desc_lock: | ||
68 | * @free_desc: | ||
69 | * @used_desc: | ||
70 | * @resource: | ||
71 | * @device_node: | ||
72 | */ | ||
73 | |||
74 | struct ioat_dma_chan { | ||
75 | |||
76 | void *reg_base; | ||
77 | |||
78 | dma_cookie_t completed_cookie; | ||
79 | unsigned long last_completion; | ||
80 | |||
81 | u32 xfercap; /* XFERCAP register value expanded out */ | ||
82 | |||
83 | spinlock_t cleanup_lock; | ||
84 | spinlock_t desc_lock; | ||
85 | struct list_head free_desc; | ||
86 | struct list_head used_desc; | ||
87 | |||
88 | int pending; | ||
89 | |||
90 | struct ioat_device *device; | ||
91 | struct dma_chan common; | ||
92 | |||
93 | dma_addr_t completion_addr; | ||
94 | union { | ||
95 | u64 full; /* HW completion writeback */ | ||
96 | struct { | ||
97 | u32 low; | ||
98 | u32 high; | ||
99 | }; | ||
100 | } *completion_virt; | ||
101 | }; | ||
102 | |||
103 | /* wrapper around hardware descriptor format + additional software fields */ | ||
104 | |||
105 | /** | ||
106 | * struct ioat_desc_sw - wrapper around hardware descriptor | ||
107 | * @hw: hardware DMA descriptor | ||
108 | * @node: | ||
109 | * @cookie: | ||
110 | * @phys: | ||
111 | */ | ||
112 | |||
113 | struct ioat_desc_sw { | ||
114 | struct ioat_dma_descriptor *hw; | ||
115 | struct list_head node; | ||
116 | dma_cookie_t cookie; | ||
117 | dma_addr_t phys; | ||
118 | DECLARE_PCI_UNMAP_ADDR(src) | ||
119 | DECLARE_PCI_UNMAP_LEN(src_len) | ||
120 | DECLARE_PCI_UNMAP_ADDR(dst) | ||
121 | DECLARE_PCI_UNMAP_LEN(dst_len) | ||
122 | }; | ||
123 | |||
124 | #endif /* IOATDMA_H */ | ||
125 | |||
diff --git a/drivers/dma/ioatdma_hw.h b/drivers/dma/ioatdma_hw.h new file mode 100644 index 00000000000..4d7a12880be --- /dev/null +++ b/drivers/dma/ioatdma_hw.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | #ifndef _IOAT_HW_H_ | ||
22 | #define _IOAT_HW_H_ | ||
23 | |||
24 | /* PCI Configuration Space Values */ | ||
25 | #define IOAT_PCI_VID 0x8086 | ||
26 | #define IOAT_PCI_DID 0x1A38 | ||
27 | #define IOAT_PCI_RID 0x00 | ||
28 | #define IOAT_PCI_SVID 0x8086 | ||
29 | #define IOAT_PCI_SID 0x8086 | ||
30 | #define IOAT_VER 0x12 /* Version 1.2 */ | ||
31 | |||
32 | struct ioat_dma_descriptor { | ||
33 | uint32_t size; | ||
34 | uint32_t ctl; | ||
35 | uint64_t src_addr; | ||
36 | uint64_t dst_addr; | ||
37 | uint64_t next; | ||
38 | uint64_t rsv1; | ||
39 | uint64_t rsv2; | ||
40 | uint64_t user1; | ||
41 | uint64_t user2; | ||
42 | }; | ||
43 | |||
44 | #define IOAT_DMA_DESCRIPTOR_CTL_INT_GN 0x00000001 | ||
45 | #define IOAT_DMA_DESCRIPTOR_CTL_SRC_SN 0x00000002 | ||
46 | #define IOAT_DMA_DESCRIPTOR_CTL_DST_SN 0x00000004 | ||
47 | #define IOAT_DMA_DESCRIPTOR_CTL_CP_STS 0x00000008 | ||
48 | #define IOAT_DMA_DESCRIPTOR_CTL_FRAME 0x00000010 | ||
49 | #define IOAT_DMA_DESCRIPTOR_NUL 0x00000020 | ||
50 | #define IOAT_DMA_DESCRIPTOR_OPCODE 0xFF000000 | ||
51 | |||
52 | #endif | ||
diff --git a/drivers/dma/ioatdma_io.h b/drivers/dma/ioatdma_io.h new file mode 100644 index 00000000000..c0b4bf66c92 --- /dev/null +++ b/drivers/dma/ioatdma_io.h | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | #ifndef IOATDMA_IO_H | ||
22 | #define IOATDMA_IO_H | ||
23 | |||
24 | #include <asm/io.h> | ||
25 | |||
26 | /* | ||
27 | * device and per-channel MMIO register read and write functions | ||
28 | * this is a lot of anoying inline functions, but it's typesafe | ||
29 | */ | ||
30 | |||
31 | static inline u8 ioatdma_read8(struct ioat_device *device, | ||
32 | unsigned int offset) | ||
33 | { | ||
34 | return readb(device->reg_base + offset); | ||
35 | } | ||
36 | |||
37 | static inline u16 ioatdma_read16(struct ioat_device *device, | ||
38 | unsigned int offset) | ||
39 | { | ||
40 | return readw(device->reg_base + offset); | ||
41 | } | ||
42 | |||
43 | static inline u32 ioatdma_read32(struct ioat_device *device, | ||
44 | unsigned int offset) | ||
45 | { | ||
46 | return readl(device->reg_base + offset); | ||
47 | } | ||
48 | |||
49 | static inline void ioatdma_write8(struct ioat_device *device, | ||
50 | unsigned int offset, u8 value) | ||
51 | { | ||
52 | writeb(value, device->reg_base + offset); | ||
53 | } | ||
54 | |||
55 | static inline void ioatdma_write16(struct ioat_device *device, | ||
56 | unsigned int offset, u16 value) | ||
57 | { | ||
58 | writew(value, device->reg_base + offset); | ||
59 | } | ||
60 | |||
61 | static inline void ioatdma_write32(struct ioat_device *device, | ||
62 | unsigned int offset, u32 value) | ||
63 | { | ||
64 | writel(value, device->reg_base + offset); | ||
65 | } | ||
66 | |||
67 | static inline u8 ioatdma_chan_read8(struct ioat_dma_chan *chan, | ||
68 | unsigned int offset) | ||
69 | { | ||
70 | return readb(chan->reg_base + offset); | ||
71 | } | ||
72 | |||
73 | static inline u16 ioatdma_chan_read16(struct ioat_dma_chan *chan, | ||
74 | unsigned int offset) | ||
75 | { | ||
76 | return readw(chan->reg_base + offset); | ||
77 | } | ||
78 | |||
79 | static inline u32 ioatdma_chan_read32(struct ioat_dma_chan *chan, | ||
80 | unsigned int offset) | ||
81 | { | ||
82 | return readl(chan->reg_base + offset); | ||
83 | } | ||
84 | |||
85 | static inline void ioatdma_chan_write8(struct ioat_dma_chan *chan, | ||
86 | unsigned int offset, u8 value) | ||
87 | { | ||
88 | writeb(value, chan->reg_base + offset); | ||
89 | } | ||
90 | |||
91 | static inline void ioatdma_chan_write16(struct ioat_dma_chan *chan, | ||
92 | unsigned int offset, u16 value) | ||
93 | { | ||
94 | writew(value, chan->reg_base + offset); | ||
95 | } | ||
96 | |||
97 | static inline void ioatdma_chan_write32(struct ioat_dma_chan *chan, | ||
98 | unsigned int offset, u32 value) | ||
99 | { | ||
100 | writel(value, chan->reg_base + offset); | ||
101 | } | ||
102 | |||
103 | #if (BITS_PER_LONG == 64) | ||
104 | static inline u64 ioatdma_chan_read64(struct ioat_dma_chan *chan, | ||
105 | unsigned int offset) | ||
106 | { | ||
107 | return readq(chan->reg_base + offset); | ||
108 | } | ||
109 | |||
110 | static inline void ioatdma_chan_write64(struct ioat_dma_chan *chan, | ||
111 | unsigned int offset, u64 value) | ||
112 | { | ||
113 | writeq(value, chan->reg_base + offset); | ||
114 | } | ||
115 | #endif | ||
116 | |||
117 | #endif /* IOATDMA_IO_H */ | ||
118 | |||
diff --git a/drivers/dma/ioatdma_registers.h b/drivers/dma/ioatdma_registers.h new file mode 100644 index 00000000000..41a21ab2b00 --- /dev/null +++ b/drivers/dma/ioatdma_registers.h | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; either version 2 of the License, or (at your option) | ||
7 | * any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | * The full GNU General Public License is included in this distribution in the | ||
19 | * file called COPYING. | ||
20 | */ | ||
21 | #ifndef _IOAT_REGISTERS_H_ | ||
22 | #define _IOAT_REGISTERS_H_ | ||
23 | |||
24 | |||
25 | /* MMIO Device Registers */ | ||
26 | #define IOAT_CHANCNT_OFFSET 0x00 /* 8-bit */ | ||
27 | |||
28 | #define IOAT_XFERCAP_OFFSET 0x01 /* 8-bit */ | ||
29 | #define IOAT_XFERCAP_4KB 12 | ||
30 | #define IOAT_XFERCAP_8KB 13 | ||
31 | #define IOAT_XFERCAP_16KB 14 | ||
32 | #define IOAT_XFERCAP_32KB 15 | ||
33 | #define IOAT_XFERCAP_32GB 0 | ||
34 | |||
35 | #define IOAT_GENCTRL_OFFSET 0x02 /* 8-bit */ | ||
36 | #define IOAT_GENCTRL_DEBUG_EN 0x01 | ||
37 | |||
38 | #define IOAT_INTRCTRL_OFFSET 0x03 /* 8-bit */ | ||
39 | #define IOAT_INTRCTRL_MASTER_INT_EN 0x01 /* Master Interrupt Enable */ | ||
40 | #define IOAT_INTRCTRL_INT_STATUS 0x02 /* ATTNSTATUS -or- Channel Int */ | ||
41 | #define IOAT_INTRCTRL_INT 0x04 /* INT_STATUS -and- MASTER_INT_EN */ | ||
42 | |||
43 | #define IOAT_ATTNSTATUS_OFFSET 0x04 /* Each bit is a channel */ | ||
44 | |||
45 | #define IOAT_VER_OFFSET 0x08 /* 8-bit */ | ||
46 | #define IOAT_VER_MAJOR_MASK 0xF0 | ||
47 | #define IOAT_VER_MINOR_MASK 0x0F | ||
48 | #define GET_IOAT_VER_MAJOR(x) ((x) & IOAT_VER_MAJOR_MASK) | ||
49 | #define GET_IOAT_VER_MINOR(x) ((x) & IOAT_VER_MINOR_MASK) | ||
50 | |||
51 | #define IOAT_PERPORTOFFSET_OFFSET 0x0A /* 16-bit */ | ||
52 | |||
53 | #define IOAT_INTRDELAY_OFFSET 0x0C /* 16-bit */ | ||
54 | #define IOAT_INTRDELAY_INT_DELAY_MASK 0x3FFF /* Interrupt Delay Time */ | ||
55 | #define IOAT_INTRDELAY_COALESE_SUPPORT 0x8000 /* Interrupt Coalesing Supported */ | ||
56 | |||
57 | #define IOAT_DEVICE_STATUS_OFFSET 0x0E /* 16-bit */ | ||
58 | #define IOAT_DEVICE_STATUS_DEGRADED_MODE 0x0001 | ||
59 | |||
60 | |||
61 | #define IOAT_CHANNEL_MMIO_SIZE 0x80 /* Each Channel MMIO space is this size */ | ||
62 | |||
63 | /* DMA Channel Registers */ | ||
64 | #define IOAT_CHANCTRL_OFFSET 0x00 /* 16-bit Channel Control Register */ | ||
65 | #define IOAT_CHANCTRL_CHANNEL_PRIORITY_MASK 0xF000 | ||
66 | #define IOAT_CHANCTRL_CHANNEL_IN_USE 0x0100 | ||
67 | #define IOAT_CHANCTRL_DESCRIPTOR_ADDR_SNOOP_CONTROL 0x0020 | ||
68 | #define IOAT_CHANCTRL_ERR_INT_EN 0x0010 | ||
69 | #define IOAT_CHANCTRL_ANY_ERR_ABORT_EN 0x0008 | ||
70 | #define IOAT_CHANCTRL_ERR_COMPLETION_EN 0x0004 | ||
71 | #define IOAT_CHANCTRL_INT_DISABLE 0x0001 | ||
72 | |||
73 | #define IOAT_DMA_COMP_OFFSET 0x02 /* 16-bit DMA channel compatability */ | ||
74 | #define IOAT_DMA_COMP_V1 0x0001 /* Compatability with DMA version 1 */ | ||
75 | |||
76 | #define IOAT_CHANSTS_OFFSET 0x04 /* 64-bit Channel Status Register */ | ||
77 | #define IOAT_CHANSTS_OFFSET_LOW 0x04 | ||
78 | #define IOAT_CHANSTS_OFFSET_HIGH 0x08 | ||
79 | #define IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR 0xFFFFFFFFFFFFFFC0 | ||
80 | #define IOAT_CHANSTS_SOFT_ERR 0x0000000000000010 | ||
81 | #define IOAT_CHANSTS_DMA_TRANSFER_STATUS 0x0000000000000007 | ||
82 | #define IOAT_CHANSTS_DMA_TRANSFER_STATUS_ACTIVE 0x0 | ||
83 | #define IOAT_CHANSTS_DMA_TRANSFER_STATUS_DONE 0x1 | ||
84 | #define IOAT_CHANSTS_DMA_TRANSFER_STATUS_SUSPENDED 0x2 | ||
85 | #define IOAT_CHANSTS_DMA_TRANSFER_STATUS_HALTED 0x3 | ||
86 | |||
87 | #define IOAT_CHAINADDR_OFFSET 0x0C /* 64-bit Descriptor Chain Address Register */ | ||
88 | #define IOAT_CHAINADDR_OFFSET_LOW 0x0C | ||
89 | #define IOAT_CHAINADDR_OFFSET_HIGH 0x10 | ||
90 | |||
91 | #define IOAT_CHANCMD_OFFSET 0x14 /* 8-bit DMA Channel Command Register */ | ||
92 | #define IOAT_CHANCMD_RESET 0x20 | ||
93 | #define IOAT_CHANCMD_RESUME 0x10 | ||
94 | #define IOAT_CHANCMD_ABORT 0x08 | ||
95 | #define IOAT_CHANCMD_SUSPEND 0x04 | ||
96 | #define IOAT_CHANCMD_APPEND 0x02 | ||
97 | #define IOAT_CHANCMD_START 0x01 | ||
98 | |||
99 | #define IOAT_CHANCMP_OFFSET 0x18 /* 64-bit Channel Completion Address Register */ | ||
100 | #define IOAT_CHANCMP_OFFSET_LOW 0x18 | ||
101 | #define IOAT_CHANCMP_OFFSET_HIGH 0x1C | ||
102 | |||
103 | #define IOAT_CDAR_OFFSET 0x20 /* 64-bit Current Descriptor Address Register */ | ||
104 | #define IOAT_CDAR_OFFSET_LOW 0x20 | ||
105 | #define IOAT_CDAR_OFFSET_HIGH 0x24 | ||
106 | |||
107 | #define IOAT_CHANERR_OFFSET 0x28 /* 32-bit Channel Error Register */ | ||
108 | #define IOAT_CHANERR_DMA_TRANSFER_SRC_ADDR_ERR 0x0001 | ||
109 | #define IOAT_CHANERR_DMA_TRANSFER_DEST_ADDR_ERR 0x0002 | ||
110 | #define IOAT_CHANERR_NEXT_DESCRIPTOR_ADDR_ERR 0x0004 | ||
111 | #define IOAT_CHANERR_NEXT_DESCRIPTOR_ALIGNMENT_ERR 0x0008 | ||
112 | #define IOAT_CHANERR_CHAIN_ADDR_VALUE_ERR 0x0010 | ||
113 | #define IOAT_CHANERR_CHANCMD_ERR 0x0020 | ||
114 | #define IOAT_CHANERR_CHIPSET_UNCORRECTABLE_DATA_INTEGRITY_ERR 0x0040 | ||
115 | #define IOAT_CHANERR_DMA_UNCORRECTABLE_DATA_INTEGRITY_ERR 0x0080 | ||
116 | #define IOAT_CHANERR_READ_DATA_ERR 0x0100 | ||
117 | #define IOAT_CHANERR_WRITE_DATA_ERR 0x0200 | ||
118 | #define IOAT_CHANERR_DESCRIPTOR_CONTROL_ERR 0x0400 | ||
119 | #define IOAT_CHANERR_DESCRIPTOR_LENGTH_ERR 0x0800 | ||
120 | #define IOAT_CHANERR_COMPLETION_ADDR_ERR 0x1000 | ||
121 | #define IOAT_CHANERR_INT_CONFIGURATION_ERR 0x2000 | ||
122 | #define IOAT_CHANERR_SOFT_ERR 0x4000 | ||
123 | |||
124 | #define IOAT_CHANERR_MASK_OFFSET 0x2C /* 32-bit Channel Error Register */ | ||
125 | |||
126 | #endif /* _IOAT_REGISTERS_H_ */ | ||
diff --git a/drivers/dma/iovlock.c b/drivers/dma/iovlock.c new file mode 100644 index 00000000000..5ed327e453a --- /dev/null +++ b/drivers/dma/iovlock.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /* | ||
2 | * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. | ||
3 | * Portions based on net/core/datagram.c and copyrighted by their authors. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the Free | ||
7 | * Software Foundation; either version 2 of the License, or (at your option) | ||
8 | * any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | * Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | * | ||
19 | * The full GNU General Public License is included in this distribution in the | ||
20 | * file called COPYING. | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * This code allows the net stack to make use of a DMA engine for | ||
25 | * skb to iovec copies. | ||
26 | */ | ||
27 | |||
28 | #include <linux/dmaengine.h> | ||
29 | #include <linux/pagemap.h> | ||
30 | #include <net/tcp.h> /* for memcpy_toiovec */ | ||
31 | #include <asm/io.h> | ||
32 | #include <asm/uaccess.h> | ||
33 | |||
34 | int num_pages_spanned(struct iovec *iov) | ||
35 | { | ||
36 | return | ||
37 | ((PAGE_ALIGN((unsigned long)iov->iov_base + iov->iov_len) - | ||
38 | ((unsigned long)iov->iov_base & PAGE_MASK)) >> PAGE_SHIFT); | ||
39 | } | ||
40 | |||
41 | /* | ||
42 | * Pin down all the iovec pages needed for len bytes. | ||
43 | * Return a struct dma_pinned_list to keep track of pages pinned down. | ||
44 | * | ||
45 | * We are allocating a single chunk of memory, and then carving it up into | ||
46 | * 3 sections, the latter 2 whose size depends on the number of iovecs and the | ||
47 | * total number of pages, respectively. | ||
48 | */ | ||
49 | struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len) | ||
50 | { | ||
51 | struct dma_pinned_list *local_list; | ||
52 | struct page **pages; | ||
53 | int i; | ||
54 | int ret; | ||
55 | int nr_iovecs = 0; | ||
56 | int iovec_len_used = 0; | ||
57 | int iovec_pages_used = 0; | ||
58 | long err; | ||
59 | |||
60 | /* don't pin down non-user-based iovecs */ | ||
61 | if (segment_eq(get_fs(), KERNEL_DS)) | ||
62 | return NULL; | ||
63 | |||
64 | /* determine how many iovecs/pages there are, up front */ | ||
65 | do { | ||
66 | iovec_len_used += iov[nr_iovecs].iov_len; | ||
67 | iovec_pages_used += num_pages_spanned(&iov[nr_iovecs]); | ||
68 | nr_iovecs++; | ||
69 | } while (iovec_len_used < len); | ||
70 | |||
71 | /* single kmalloc for pinned list, page_list[], and the page arrays */ | ||
72 | local_list = kmalloc(sizeof(*local_list) | ||
73 | + (nr_iovecs * sizeof (struct dma_page_list)) | ||
74 | + (iovec_pages_used * sizeof (struct page*)), GFP_KERNEL); | ||
75 | if (!local_list) { | ||
76 | err = -ENOMEM; | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | /* list of pages starts right after the page list array */ | ||
81 | pages = (struct page **) &local_list->page_list[nr_iovecs]; | ||
82 | |||
83 | for (i = 0; i < nr_iovecs; i++) { | ||
84 | struct dma_page_list *page_list = &local_list->page_list[i]; | ||
85 | |||
86 | len -= iov[i].iov_len; | ||
87 | |||
88 | if (!access_ok(VERIFY_WRITE, iov[i].iov_base, iov[i].iov_len)) { | ||
89 | err = -EFAULT; | ||
90 | goto unpin; | ||
91 | } | ||
92 | |||
93 | page_list->nr_pages = num_pages_spanned(&iov[i]); | ||
94 | page_list->base_address = iov[i].iov_base; | ||
95 | |||
96 | page_list->pages = pages; | ||
97 | pages += page_list->nr_pages; | ||
98 | |||
99 | /* pin pages down */ | ||
100 | down_read(¤t->mm->mmap_sem); | ||
101 | ret = get_user_pages( | ||
102 | current, | ||
103 | current->mm, | ||
104 | (unsigned long) iov[i].iov_base, | ||
105 | page_list->nr_pages, | ||
106 | 1, /* write */ | ||
107 | 0, /* force */ | ||
108 | page_list->pages, | ||
109 | NULL); | ||
110 | up_read(¤t->mm->mmap_sem); | ||
111 | |||
112 | if (ret != page_list->nr_pages) { | ||
113 | err = -ENOMEM; | ||
114 | goto unpin; | ||
115 | } | ||
116 | |||
117 | local_list->nr_iovecs = i + 1; | ||
118 | } | ||
119 | |||
120 | return local_list; | ||
121 | |||
122 | unpin: | ||
123 | dma_unpin_iovec_pages(local_list); | ||
124 | out: | ||
125 | return ERR_PTR(err); | ||
126 | } | ||
127 | |||
128 | void dma_unpin_iovec_pages(struct dma_pinned_list *pinned_list) | ||
129 | { | ||
130 | int i, j; | ||
131 | |||
132 | if (!pinned_list) | ||
133 | return; | ||
134 | |||
135 | for (i = 0; i < pinned_list->nr_iovecs; i++) { | ||
136 | struct dma_page_list *page_list = &pinned_list->page_list[i]; | ||
137 | for (j = 0; j < page_list->nr_pages; j++) { | ||
138 | set_page_dirty_lock(page_list->pages[j]); | ||
139 | page_cache_release(page_list->pages[j]); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | kfree(pinned_list); | ||
144 | } | ||
145 | |||
146 | static dma_cookie_t dma_memcpy_to_kernel_iovec(struct dma_chan *chan, struct | ||
147 | iovec *iov, unsigned char *kdata, size_t len) | ||
148 | { | ||
149 | dma_cookie_t dma_cookie = 0; | ||
150 | |||
151 | while (len > 0) { | ||
152 | if (iov->iov_len) { | ||
153 | int copy = min_t(unsigned int, iov->iov_len, len); | ||
154 | dma_cookie = dma_async_memcpy_buf_to_buf( | ||
155 | chan, | ||
156 | iov->iov_base, | ||
157 | kdata, | ||
158 | copy); | ||
159 | kdata += copy; | ||
160 | len -= copy; | ||
161 | iov->iov_len -= copy; | ||
162 | iov->iov_base += copy; | ||
163 | } | ||
164 | iov++; | ||
165 | } | ||
166 | |||
167 | return dma_cookie; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * We have already pinned down the pages we will be using in the iovecs. | ||
172 | * Each entry in iov array has corresponding entry in pinned_list->page_list. | ||
173 | * Using array indexing to keep iov[] and page_list[] in sync. | ||
174 | * Initial elements in iov array's iov->iov_len will be 0 if already copied into | ||
175 | * by another call. | ||
176 | * iov array length remaining guaranteed to be bigger than len. | ||
177 | */ | ||
178 | dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov, | ||
179 | struct dma_pinned_list *pinned_list, unsigned char *kdata, size_t len) | ||
180 | { | ||
181 | int iov_byte_offset; | ||
182 | int copy; | ||
183 | dma_cookie_t dma_cookie = 0; | ||
184 | int iovec_idx; | ||
185 | int page_idx; | ||
186 | |||
187 | if (!chan) | ||
188 | return memcpy_toiovec(iov, kdata, len); | ||
189 | |||
190 | /* -> kernel copies (e.g. smbfs) */ | ||
191 | if (!pinned_list) | ||
192 | return dma_memcpy_to_kernel_iovec(chan, iov, kdata, len); | ||
193 | |||
194 | iovec_idx = 0; | ||
195 | while (iovec_idx < pinned_list->nr_iovecs) { | ||
196 | struct dma_page_list *page_list; | ||
197 | |||
198 | /* skip already used-up iovecs */ | ||
199 | while (!iov[iovec_idx].iov_len) | ||
200 | iovec_idx++; | ||
201 | |||
202 | page_list = &pinned_list->page_list[iovec_idx]; | ||
203 | |||
204 | iov_byte_offset = ((unsigned long)iov[iovec_idx].iov_base & ~PAGE_MASK); | ||
205 | page_idx = (((unsigned long)iov[iovec_idx].iov_base & PAGE_MASK) | ||
206 | - ((unsigned long)page_list->base_address & PAGE_MASK)) >> PAGE_SHIFT; | ||
207 | |||
208 | /* break up copies to not cross page boundary */ | ||
209 | while (iov[iovec_idx].iov_len) { | ||
210 | copy = min_t(int, PAGE_SIZE - iov_byte_offset, len); | ||
211 | copy = min_t(int, copy, iov[iovec_idx].iov_len); | ||
212 | |||
213 | dma_cookie = dma_async_memcpy_buf_to_pg(chan, | ||
214 | page_list->pages[page_idx], | ||
215 | iov_byte_offset, | ||
216 | kdata, | ||
217 | copy); | ||
218 | |||
219 | len -= copy; | ||
220 | iov[iovec_idx].iov_len -= copy; | ||
221 | iov[iovec_idx].iov_base += copy; | ||
222 | |||
223 | if (!len) | ||
224 | return dma_cookie; | ||
225 | |||
226 | kdata += copy; | ||
227 | iov_byte_offset = 0; | ||
228 | page_idx++; | ||
229 | } | ||
230 | iovec_idx++; | ||
231 | } | ||
232 | |||
233 | /* really bad if we ever run out of iovecs */ | ||
234 | BUG(); | ||
235 | return -EFAULT; | ||
236 | } | ||
237 | |||
238 | dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov, | ||
239 | struct dma_pinned_list *pinned_list, struct page *page, | ||
240 | unsigned int offset, size_t len) | ||
241 | { | ||
242 | int iov_byte_offset; | ||
243 | int copy; | ||
244 | dma_cookie_t dma_cookie = 0; | ||
245 | int iovec_idx; | ||
246 | int page_idx; | ||
247 | int err; | ||
248 | |||
249 | /* this needs as-yet-unimplemented buf-to-buff, so punt. */ | ||
250 | /* TODO: use dma for this */ | ||
251 | if (!chan || !pinned_list) { | ||
252 | u8 *vaddr = kmap(page); | ||
253 | err = memcpy_toiovec(iov, vaddr + offset, len); | ||
254 | kunmap(page); | ||
255 | return err; | ||
256 | } | ||
257 | |||
258 | iovec_idx = 0; | ||
259 | while (iovec_idx < pinned_list->nr_iovecs) { | ||
260 | struct dma_page_list *page_list; | ||
261 | |||
262 | /* skip already used-up iovecs */ | ||
263 | while (!iov[iovec_idx].iov_len) | ||
264 | iovec_idx++; | ||
265 | |||
266 | page_list = &pinned_list->page_list[iovec_idx]; | ||
267 | |||
268 | iov_byte_offset = ((unsigned long)iov[iovec_idx].iov_base & ~PAGE_MASK); | ||
269 | page_idx = (((unsigned long)iov[iovec_idx].iov_base & PAGE_MASK) | ||
270 | - ((unsigned long)page_list->base_address & PAGE_MASK)) >> PAGE_SHIFT; | ||
271 | |||
272 | /* break up copies to not cross page boundary */ | ||
273 | while (iov[iovec_idx].iov_len) { | ||
274 | copy = min_t(int, PAGE_SIZE - iov_byte_offset, len); | ||
275 | copy = min_t(int, copy, iov[iovec_idx].iov_len); | ||
276 | |||
277 | dma_cookie = dma_async_memcpy_pg_to_pg(chan, | ||
278 | page_list->pages[page_idx], | ||
279 | iov_byte_offset, | ||
280 | page, | ||
281 | offset, | ||
282 | copy); | ||
283 | |||
284 | len -= copy; | ||
285 | iov[iovec_idx].iov_len -= copy; | ||
286 | iov[iovec_idx].iov_base += copy; | ||
287 | |||
288 | if (!len) | ||
289 | return dma_cookie; | ||
290 | |||
291 | offset += copy; | ||
292 | iov_byte_offset = 0; | ||
293 | page_idx++; | ||
294 | } | ||
295 | iovec_idx++; | ||
296 | } | ||
297 | |||
298 | /* really bad if we ever run out of iovecs */ | ||
299 | BUG(); | ||
300 | return -EFAULT; | ||
301 | } | ||