1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/init.h>
14#include <linux/console.h>
15#include <linux/pci.h>
16#include <linux/version.h>
17#include <linux/module.h>
18#include <asm/rtas.h>
19#include <asm/pci_dma.h>
20#include <asm/dma.h>
21#include <asm/ppcdebug.h>
22#include <asm/vio.h>
23#include <asm/hvcall.h>
24
25extern struct TceTable *build_tce_table(struct TceTable *tbl);
26
27extern dma_addr_t get_tces(struct TceTable *, unsigned order,
28 void *page, unsigned numPages, int direction);
29extern void tce_free(struct TceTable *tbl, dma_addr_t dma_addr,
30 unsigned order, unsigned num_pages);
31
32
33static struct vio_bus vio_bus;
34static LIST_HEAD(registered_vio_drivers);
35int vio_num_address_cells;
36EXPORT_SYMBOL(vio_num_address_cells);
37
38
39
40
41
42
43
44
45
46
47int vio_register_driver(struct vio_driver *drv)
48{
49 int count = 0;
50 struct vio_dev *dev;
51 const struct vio_device_id* id;
52
53
54
55
56
57 list_for_each_entry(dev, &vio_bus.devices, devices_list) {
58 id = vio_match_device(drv->id_table, dev);
59 if(drv && id) {
60 if (0 == drv->probe(dev, id)) {
61 dev->driver = drv;
62 count++;
63 }
64 }
65 }
66
67 list_add_tail(&drv->node, ®istered_vio_drivers);
68
69 return count;
70}
71EXPORT_SYMBOL(vio_register_driver);
72
73
74
75
76
77
78
79
80
81
82int vio_unregister_driver(struct vio_driver *driver)
83{
84 struct vio_dev *dev;
85 int devices_found = 0;
86
87 list_for_each_entry(dev, &vio_bus.devices, devices_list) {
88 if (dev->driver == driver) {
89 driver->remove(dev);
90 dev->driver = NULL;
91 devices_found++;
92 }
93 }
94
95 list_del(&driver->node);
96
97 return devices_found;
98}
99EXPORT_SYMBOL(vio_unregister_driver);
100
101
102
103
104
105
106
107
108
109
110const struct vio_device_id *
111vio_match_device(const struct vio_device_id *ids, const struct vio_dev *dev)
112{
113 while (ids->type) {
114 if ((strncmp(dev->archdata->type, ids->type, strlen(ids->type)) == 0) &&
115 device_is_compatible((struct device_node*)dev->archdata, ids->compat))
116 return ids;
117 ids++;
118 }
119 return NULL;
120}
121
122
123
124
125int __init
126vio_bus_init(void)
127{
128 struct device_node *node_vroot, *node_vdev;
129
130 printk("vio_bus_init: start\n");
131
132 INIT_LIST_HEAD(&vio_bus.devices);
133
134
135
136
137
138
139 node_vroot = find_devices("vdevice");
140 if (!node_vroot) {
141 printk(KERN_WARNING "vio_bus_init: no /vdevice node\n");
142 return 0;
143 }
144
145 vio_num_address_cells = prom_n_addr_cells(node_vroot->child);
146
147 for (node_vdev = node_vroot->child;
148 node_vdev != NULL;
149 node_vdev = node_vdev->sibling) {
150 printk(KERN_INFO "vio_bus_init: processing %p\n", node_vdev);
151
152 vio_register_device(node_vdev);
153 }
154
155 printk(KERN_INFO "vio_bus_init: done\n");
156
157 return 0;
158}
159
160__initcall(vio_bus_init);
161
162
163
164
165
166
167
168
169
170
171
172struct vio_dev * __devinit vio_register_device(struct device_node *node_vdev)
173{
174 struct vio_dev *dev;
175 unsigned int *unit_address;
176 unsigned int *irq_p;
177
178
179 if ((NULL == node_vdev->type)) {
180 printk(KERN_WARNING "vio_register_device: node %s missing 'device_type' "
181 , node_vdev->name?node_vdev->name:"UNKNOWN");
182 return NULL;
183 }
184
185 unit_address = (unsigned int *)get_property(node_vdev, "reg", NULL);
186 if(!unit_address) {
187 printk(KERN_WARNING "Can't find %s reg property\n", node_vdev->name?node_vdev->name:"UNKNOWN_DEVICE");
188 return NULL;
189 }
190
191
192 dev = kmalloc(sizeof(*dev), GFP_KERNEL);
193 memset(dev, 0, sizeof(*dev));
194
195 dev->archdata = (void*)node_vdev;
196 dev->bus = &vio_bus;
197 dev->unit_address = *unit_address;
198 dev->tce_table = vio_build_tce_table(dev);
199
200 irq_p = (unsigned int *) get_property(node_vdev, "interrupts", 0);
201 if(irq_p) {
202 dev->irq = irq_offset_up(*irq_p);
203 } else {
204 dev->irq = (unsigned int) -1;
205 }
206
207 list_add_tail(&dev->devices_list, &vio_bus.devices);
208
209 return dev;
210}
211
212
213
214
215
216
217
218
219
220
221
222struct vio_driver * vio_find_driver(struct vio_dev* dev)
223{
224 struct vio_driver *driver;
225 list_for_each_entry(driver, ®istered_vio_drivers, node) {
226 if(driver && vio_match_device(driver->id_table, dev)) {
227 if (0 < driver->probe(dev, NULL)) {
228 dev->driver = driver;
229 return driver;
230 }
231 }
232 }
233
234 return NULL;
235}
236
237
238
239
240
241
242
243
244
245
246const void * vio_get_attribute(struct vio_dev *vdev, void* which, int* length)
247{
248 return get_property((struct device_node *)vdev->archdata, (char*)which, length);
249}
250EXPORT_SYMBOL(vio_get_attribute);
251
252
253
254
255
256
257
258
259struct TceTable * vio_build_tce_table(struct vio_dev *dev)
260{
261 unsigned int *dma_window;
262 struct TceTable *newTceTable;
263 unsigned long offset;
264 unsigned long size;
265 int dma_window_property_size;
266
267 dma_window = (unsigned int *) get_property((struct device_node *)dev->archdata, "ibm,my-dma-window", &dma_window_property_size);
268 if(!dma_window) {
269 return NULL;
270 }
271
272 newTceTable = (struct TceTable *) kmalloc(sizeof(struct TceTable), GFP_KERNEL);
273
274
275
276
277
278 size = ((dma_window[1+vio_num_address_cells]
279 >> PAGE_SHIFT) << 3) >> PAGE_SHIFT;
280
281
282
283
284 if (dma_window_property_size == 12) {
285 size = ((dma_window[1] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT;
286 } else if (dma_window_property_size == 20) {
287 size = ((dma_window[4] >> PAGE_SHIFT) << 3) >> PAGE_SHIFT;
288 } else {
289 printk(KERN_WARNING "vio_build_tce_table: Invalid size of ibm,my-dma-window=%i, using 0x80 for size\n", dma_window_property_size);
290 size = 0x80;
291 }
292
293
294
295
296 offset = dma_window[1] >> PAGE_SHIFT;
297
298
299 newTceTable->size = size;
300
301 newTceTable->startOffset = offset;
302 newTceTable->busNumber = 0;
303 newTceTable->index = (unsigned long)dma_window[0];
304 newTceTable->tceType = TCE_VB;
305
306 return build_tce_table(newTceTable);
307}
308
309int vio_enable_interrupts(struct vio_dev *dev)
310{
311 int rc = h_vio_signal(dev->unit_address, VIO_IRQ_ENABLE);
312 if (rc != H_Success) {
313 printk(KERN_ERR "vio: Error 0x%x enabling interrupts\n", rc);
314 }
315 return rc;
316}
317EXPORT_SYMBOL(vio_enable_interrupts);
318
319int vio_disable_interrupts(struct vio_dev *dev)
320{
321 int rc = h_vio_signal(dev->unit_address, VIO_IRQ_DISABLE);
322 if (rc != H_Success) {
323 printk(KERN_ERR "vio: Error 0x%x disabling interrupts\n", rc);
324 }
325 return rc;
326}
327EXPORT_SYMBOL(vio_disable_interrupts);
328
329dma_addr_t vio_map_single(struct vio_dev *dev, void *vaddr,
330 size_t size, int direction )
331{
332 struct TceTable * tbl;
333 dma_addr_t dma_handle = NO_TCE;
334 unsigned long uaddr;
335 unsigned order, nPages;
336
337 if(direction == PCI_DMA_NONE) BUG();
338
339 uaddr = (unsigned long)vaddr;
340 nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK );
341 order = get_order( nPages & PAGE_MASK );
342 nPages >>= PAGE_SHIFT;
343
344
345
346 if(order >= NUM_TCE_LEVELS) {
347 printk("VIO_DMA: vio_map_single size to large: 0x%lx \n",size);
348 return NO_TCE;
349 }
350
351 tbl = dev->tce_table;
352
353 if(tbl) {
354 dma_handle = get_tces(tbl, order, vaddr, nPages, direction);
355 dma_handle |= (uaddr & ~PAGE_MASK);
356 }
357
358 return dma_handle;
359}
360EXPORT_SYMBOL(vio_map_single);
361
362void vio_unmap_single(struct vio_dev *dev, dma_addr_t dma_handle,
363 size_t size, int direction)
364{
365 struct TceTable * tbl;
366 unsigned order, nPages;
367
368 if (direction == PCI_DMA_NONE) BUG();
369
370 nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK );
371 order = get_order( nPages & PAGE_MASK );
372 nPages >>= PAGE_SHIFT;
373
374
375
376 if(order >= NUM_TCE_LEVELS) {
377 printk("VIO_DMA: vio_unmap_single 0x%lx size to large: 0x%lx \n",(unsigned long)dma_handle,(unsigned long)size);
378 return;
379 }
380
381 tbl = dev->tce_table;
382 if(tbl) tce_free(tbl, dma_handle, order, nPages);
383}
384EXPORT_SYMBOL(vio_unmap_single);
385
386int vio_map_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems,
387 int direction)
388{
389 int i;
390
391 for (i = 0; i < nelems; i++) {
392
393
394
395 void *vaddr;
396 if (sglist->address)
397 vaddr = sglist->address;
398 else
399 vaddr = page_address(sglist->page) + sglist->offset;
400
401 sglist->dma_address = vio_map_single(vdev, vaddr,
402 sglist->length,
403 direction);
404 sglist->dma_length = sglist->length;
405 sglist++;
406 }
407
408 return nelems;
409}
410EXPORT_SYMBOL(vio_map_sg);
411
412void vio_unmap_sg(struct vio_dev *vdev, struct scatterlist *sglist, int nelems,
413 int direction)
414{
415 while (nelems--) {
416 vio_unmap_single(vdev, sglist->dma_address,
417 sglist->dma_length, direction);
418 sglist++;
419 }
420}
421
422void *vio_alloc_consistent(struct vio_dev *dev, size_t size,
423 dma_addr_t *dma_handle)
424{
425 struct TceTable * tbl;
426 void *ret = NULL;
427 unsigned order, nPages;
428 dma_addr_t tce;
429
430 size = PAGE_ALIGN(size);
431 order = get_order(size);
432 nPages = 1 << order;
433
434
435
436 if(order >= NUM_TCE_LEVELS) {
437 printk("VIO_DMA: vio_alloc_consistent size to large: 0x%lx \n",size);
438 return (void *)NO_TCE;
439 }
440
441 tbl = dev->tce_table;
442
443 if ( tbl ) {
444
445 ret = (void *)__get_free_pages( GFP_ATOMIC, order );
446 if ( ret ) {
447
448 memset(ret, 0, nPages << PAGE_SHIFT);
449
450 tce = get_tces( tbl, order, ret, nPages, PCI_DMA_BIDIRECTIONAL );
451 if ( tce == NO_TCE ) {
452 PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: get_tces failed\n" );
453 free_pages( (unsigned long)ret, order );
454 ret = NULL;
455 }
456 else
457 {
458 *dma_handle = tce;
459 }
460 }
461 else PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: __get_free_pages failed for order = %d\n", order);
462 }
463 else PPCDBG(PPCDBG_TCE, "vio_alloc_consistent: get_tce_table failed for 0x%016lx\n", dev);
464
465 PPCDBG(PPCDBG_TCE, "\tvio_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle);
466 PPCDBG(PPCDBG_TCE, "\tvio_alloc_consistent: return = 0x%16.16lx\n", ret);
467 return ret;
468}
469EXPORT_SYMBOL(vio_alloc_consistent);
470
471void vio_free_consistent(struct vio_dev *dev, size_t size,
472 void *vaddr, dma_addr_t dma_handle)
473{
474 struct TceTable * tbl;
475 unsigned order, nPages;
476
477 PPCDBG(PPCDBG_TCE, "vio_free_consistent:\n");
478 PPCDBG(PPCDBG_TCE, "\tdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", dev, size, dma_handle, vaddr);
479
480 size = PAGE_ALIGN(size);
481 order = get_order(size);
482 nPages = 1 << order;
483
484
485
486 if(order >= NUM_TCE_LEVELS) {
487 printk("PCI_DMA: pci_free_consistent size to large: 0x%lx \n",size);
488 return;
489 }
490
491 tbl = dev->tce_table;
492
493 if ( tbl ) {
494 tce_free(tbl, dma_handle, order, nPages);
495 free_pages( (unsigned long)vaddr, order );
496 }
497}
498EXPORT_SYMBOL(vio_free_consistent);
499
500EXPORT_SYMBOL(plpar_hcall_norets);
501EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
502
503
504