Commit | Line | Data |
---|---|---|
a17a75e2 MW |
1 | /* |
2 | * VME Bridge Framework | |
3 | * | |
4 | * Author: Martyn Welch <martyn.welch@gefanuc.com> | |
5 | * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc. | |
6 | * | |
7 | * Based on work by Tom Armistead and Ajit Prem | |
8 | * Copyright 2004 Motorola Inc. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | */ | |
15 | ||
16 | #include <linux/version.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/moduleparam.h> | |
19 | #include <linux/mm.h> | |
20 | #include <linux/types.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/errno.h> | |
23 | #include <linux/pci.h> | |
24 | #include <linux/poll.h> | |
25 | #include <linux/highmem.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/pagemap.h> | |
28 | #include <linux/device.h> | |
29 | #include <linux/dma-mapping.h> | |
30 | #include <linux/syscalls.h> | |
31 | #include <linux/semaphore.h> | |
32 | #include <linux/spinlock.h> | |
33 | ||
34 | #include "vme.h" | |
35 | #include "vme_bridge.h" | |
36 | ||
37 | /* Bitmask and semaphore to keep track of bridge numbers */ | |
38 | static unsigned int vme_bus_numbers; | |
39 | DECLARE_MUTEX(vme_bus_num_sem); | |
40 | ||
41 | static void __exit vme_exit (void); | |
42 | static int __init vme_init (void); | |
43 | ||
44 | ||
45 | /* | |
46 | * Find the bridge resource associated with a specific device resource | |
47 | */ | |
48 | static struct vme_bridge *dev_to_bridge(struct device *dev) | |
49 | { | |
50 | return dev->platform_data; | |
51 | } | |
52 | ||
53 | /* | |
54 | * Find the bridge that the resource is associated with. | |
55 | */ | |
56 | static struct vme_bridge *find_bridge(struct vme_resource *resource) | |
57 | { | |
58 | /* Get list to search */ | |
59 | switch (resource->type) { | |
60 | case VME_MASTER: | |
61 | return list_entry(resource->entry, struct vme_master_resource, | |
62 | list)->parent; | |
63 | break; | |
64 | case VME_SLAVE: | |
65 | return list_entry(resource->entry, struct vme_slave_resource, | |
66 | list)->parent; | |
67 | break; | |
68 | case VME_DMA: | |
69 | return list_entry(resource->entry, struct vme_dma_resource, | |
70 | list)->parent; | |
71 | break; | |
72 | default: | |
73 | printk(KERN_ERR "Unknown resource type\n"); | |
74 | return NULL; | |
75 | break; | |
76 | } | |
77 | } | |
78 | ||
79 | /* | |
80 | * Allocate a contiguous block of memory for use by the driver. This is used to | |
81 | * create the buffers for the slave windows. | |
82 | * | |
83 | * XXX VME bridges could be available on buses other than PCI. At the momment | |
84 | * this framework only supports PCI devices. | |
85 | */ | |
86 | void * vme_alloc_consistent(struct vme_resource *resource, size_t size, | |
87 | dma_addr_t *dma) | |
88 | { | |
89 | struct vme_bridge *bridge; | |
90 | struct pci_dev *pdev; | |
91 | ||
92 | if(resource == NULL) { | |
93 | printk("No resource\n"); | |
94 | return NULL; | |
95 | } | |
96 | ||
97 | bridge = find_bridge(resource); | |
98 | if(bridge == NULL) { | |
99 | printk("Can't find bridge\n"); | |
100 | return NULL; | |
101 | } | |
102 | ||
103 | /* Find pci_dev container of dev */ | |
104 | if (bridge->parent == NULL) { | |
105 | printk("Dev entry NULL\n"); | |
106 | return NULL; | |
107 | } | |
108 | pdev = container_of(bridge->parent, struct pci_dev, dev); | |
109 | ||
110 | return pci_alloc_consistent(pdev, size, dma); | |
111 | } | |
112 | EXPORT_SYMBOL(vme_alloc_consistent); | |
113 | ||
114 | /* | |
115 | * Free previously allocated contiguous block of memory. | |
116 | * | |
117 | * XXX VME bridges could be available on buses other than PCI. At the momment | |
118 | * this framework only supports PCI devices. | |
119 | */ | |
120 | void vme_free_consistent(struct vme_resource *resource, size_t size, | |
121 | void *vaddr, dma_addr_t dma) | |
122 | { | |
123 | struct vme_bridge *bridge; | |
124 | struct pci_dev *pdev; | |
125 | ||
126 | if(resource == NULL) { | |
127 | printk("No resource\n"); | |
128 | return; | |
129 | } | |
130 | ||
131 | bridge = find_bridge(resource); | |
132 | if(bridge == NULL) { | |
133 | printk("Can't find bridge\n"); | |
134 | return; | |
135 | } | |
136 | ||
137 | /* Find pci_dev container of dev */ | |
138 | pdev = container_of(bridge->parent, struct pci_dev, dev); | |
139 | ||
140 | pci_free_consistent(pdev, size, vaddr, dma); | |
141 | } | |
142 | EXPORT_SYMBOL(vme_free_consistent); | |
143 | ||
144 | size_t vme_get_size(struct vme_resource *resource) | |
145 | { | |
146 | int enabled, retval; | |
147 | unsigned long long base, size; | |
148 | dma_addr_t buf_base; | |
149 | vme_address_t aspace; | |
150 | vme_cycle_t cycle; | |
151 | vme_width_t dwidth; | |
152 | ||
153 | switch (resource->type) { | |
154 | case VME_MASTER: | |
155 | retval = vme_master_get(resource, &enabled, &base, &size, | |
156 | &aspace, &cycle, &dwidth); | |
157 | ||
158 | return size; | |
159 | break; | |
160 | case VME_SLAVE: | |
161 | retval = vme_slave_get(resource, &enabled, &base, &size, | |
162 | &buf_base, &aspace, &cycle); | |
163 | ||
164 | return size; | |
165 | break; | |
166 | case VME_DMA: | |
167 | return 0; | |
168 | break; | |
169 | default: | |
170 | printk(KERN_ERR "Unknown resource type\n"); | |
171 | return 0; | |
172 | break; | |
173 | } | |
174 | } | |
175 | EXPORT_SYMBOL(vme_get_size); | |
176 | ||
177 | static int vme_check_window(vme_address_t aspace, unsigned long long vme_base, | |
178 | unsigned long long size) | |
179 | { | |
180 | int retval = 0; | |
181 | ||
182 | switch (aspace) { | |
183 | case VME_A16: | |
184 | if (((vme_base + size) > VME_A16_MAX) || | |
185 | (vme_base > VME_A16_MAX)) | |
186 | retval = -EFAULT; | |
187 | break; | |
188 | case VME_A24: | |
189 | if (((vme_base + size) > VME_A24_MAX) || | |
190 | (vme_base > VME_A24_MAX)) | |
191 | retval = -EFAULT; | |
192 | break; | |
193 | case VME_A32: | |
194 | if (((vme_base + size) > VME_A32_MAX) || | |
195 | (vme_base > VME_A32_MAX)) | |
196 | retval = -EFAULT; | |
197 | break; | |
198 | case VME_A64: | |
199 | /* | |
200 | * Any value held in an unsigned long long can be used as the | |
201 | * base | |
202 | */ | |
203 | break; | |
204 | case VME_CRCSR: | |
205 | if (((vme_base + size) > VME_CRCSR_MAX) || | |
206 | (vme_base > VME_CRCSR_MAX)) | |
207 | retval = -EFAULT; | |
208 | break; | |
209 | case VME_USER1: | |
210 | case VME_USER2: | |
211 | case VME_USER3: | |
212 | case VME_USER4: | |
213 | /* User Defined */ | |
214 | break; | |
215 | default: | |
216 | printk("Invalid address space\n"); | |
217 | retval = -EINVAL; | |
218 | break; | |
219 | } | |
220 | ||
221 | return retval; | |
222 | } | |
223 | ||
224 | /* | |
225 | * Request a slave image with specific attributes, return some unique | |
226 | * identifier. | |
227 | */ | |
228 | struct vme_resource * vme_slave_request(struct device *dev, | |
229 | vme_address_t address, vme_cycle_t cycle) | |
230 | { | |
231 | struct vme_bridge *bridge; | |
232 | struct list_head *slave_pos = NULL; | |
233 | struct vme_slave_resource *allocated_image = NULL; | |
234 | struct vme_slave_resource *slave_image = NULL; | |
235 | struct vme_resource *resource = NULL; | |
236 | ||
237 | bridge = dev_to_bridge(dev); | |
238 | if (bridge == NULL) { | |
239 | printk(KERN_ERR "Can't find VME bus\n"); | |
240 | goto err_bus; | |
241 | } | |
242 | ||
243 | /* Loop through slave resources */ | |
244 | list_for_each(slave_pos, &(bridge->slave_resources)) { | |
245 | slave_image = list_entry(slave_pos, | |
246 | struct vme_slave_resource, list); | |
247 | ||
248 | if (slave_image == NULL) { | |
249 | printk("Registered NULL Slave resource\n"); | |
250 | continue; | |
251 | } | |
252 | ||
253 | /* Find an unlocked and compatible image */ | |
254 | down(&(slave_image->sem)); | |
255 | if(((slave_image->address_attr & address) == address) && | |
256 | ((slave_image->cycle_attr & cycle) == cycle) && | |
257 | (slave_image->locked == 0)) { | |
258 | ||
259 | slave_image->locked = 1; | |
260 | up(&(slave_image->sem)); | |
261 | allocated_image = slave_image; | |
262 | break; | |
263 | } | |
264 | up(&(slave_image->sem)); | |
265 | } | |
266 | ||
267 | /* No free image */ | |
268 | if (allocated_image == NULL) | |
269 | goto err_image; | |
270 | ||
271 | resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); | |
272 | if (resource == NULL) { | |
273 | printk(KERN_WARNING "Unable to allocate resource structure\n"); | |
274 | goto err_alloc; | |
275 | } | |
276 | resource->type = VME_SLAVE; | |
277 | resource->entry = &(allocated_image->list); | |
278 | ||
279 | return resource; | |
280 | ||
281 | err_alloc: | |
282 | /* Unlock image */ | |
283 | down(&(slave_image->sem)); | |
284 | slave_image->locked = 0; | |
285 | up(&(slave_image->sem)); | |
286 | err_image: | |
287 | err_bus: | |
288 | return NULL; | |
289 | } | |
290 | EXPORT_SYMBOL(vme_slave_request); | |
291 | ||
292 | int vme_slave_set (struct vme_resource *resource, int enabled, | |
293 | unsigned long long vme_base, unsigned long long size, | |
294 | dma_addr_t buf_base, vme_address_t aspace, vme_cycle_t cycle) | |
295 | { | |
296 | struct vme_bridge *bridge = find_bridge(resource); | |
297 | struct vme_slave_resource *image; | |
298 | int retval; | |
299 | ||
300 | if (resource->type != VME_SLAVE) { | |
301 | printk("Not a slave resource\n"); | |
302 | return -EINVAL; | |
303 | } | |
304 | ||
305 | image = list_entry(resource->entry, struct vme_slave_resource, list); | |
306 | ||
307 | if (bridge->slave_set == NULL) { | |
308 | printk("Function not supported\n"); | |
309 | return -ENOSYS; | |
310 | } | |
311 | ||
312 | if(!(((image->address_attr & aspace) == aspace) && | |
313 | ((image->cycle_attr & cycle) == cycle))) { | |
314 | printk("Invalid attributes\n"); | |
315 | return -EINVAL; | |
316 | } | |
317 | ||
318 | retval = vme_check_window(aspace, vme_base, size); | |
319 | if(retval) | |
320 | return retval; | |
321 | ||
322 | return bridge->slave_set(image, enabled, vme_base, size, buf_base, | |
323 | aspace, cycle); | |
324 | } | |
325 | EXPORT_SYMBOL(vme_slave_set); | |
326 | ||
327 | int vme_slave_get (struct vme_resource *resource, int *enabled, | |
328 | unsigned long long *vme_base, unsigned long long *size, | |
329 | dma_addr_t *buf_base, vme_address_t *aspace, vme_cycle_t *cycle) | |
330 | { | |
331 | struct vme_bridge *bridge = find_bridge(resource); | |
332 | struct vme_slave_resource *image; | |
333 | ||
334 | if (resource->type != VME_SLAVE) { | |
335 | printk("Not a slave resource\n"); | |
336 | return -EINVAL; | |
337 | } | |
338 | ||
339 | image = list_entry(resource->entry, struct vme_slave_resource, list); | |
340 | ||
341 | if (bridge->slave_set == NULL) { | |
342 | printk("vme_slave_get not supported\n"); | |
343 | return -EINVAL; | |
344 | } | |
345 | ||
346 | return bridge->slave_get(image, enabled, vme_base, size, buf_base, | |
347 | aspace, cycle); | |
348 | } | |
349 | EXPORT_SYMBOL(vme_slave_get); | |
350 | ||
351 | void vme_slave_free(struct vme_resource *resource) | |
352 | { | |
353 | struct vme_slave_resource *slave_image; | |
354 | ||
355 | if (resource->type != VME_SLAVE) { | |
356 | printk("Not a slave resource\n"); | |
357 | return; | |
358 | } | |
359 | ||
360 | slave_image = list_entry(resource->entry, struct vme_slave_resource, | |
361 | list); | |
362 | if (slave_image == NULL) { | |
363 | printk("Can't find slave resource\n"); | |
364 | return; | |
365 | } | |
366 | ||
367 | /* Unlock image */ | |
368 | down(&(slave_image->sem)); | |
369 | if (slave_image->locked == 0) | |
370 | printk(KERN_ERR "Image is already free\n"); | |
371 | ||
372 | slave_image->locked = 0; | |
373 | up(&(slave_image->sem)); | |
374 | ||
375 | /* Free up resource memory */ | |
376 | kfree(resource); | |
377 | } | |
378 | EXPORT_SYMBOL(vme_slave_free); | |
379 | ||
380 | /* | |
381 | * Request a master image with specific attributes, return some unique | |
382 | * identifier. | |
383 | */ | |
384 | struct vme_resource * vme_master_request(struct device *dev, | |
385 | vme_address_t address, vme_cycle_t cycle, vme_width_t dwidth) | |
386 | { | |
387 | struct vme_bridge *bridge; | |
388 | struct list_head *master_pos = NULL; | |
389 | struct vme_master_resource *allocated_image = NULL; | |
390 | struct vme_master_resource *master_image = NULL; | |
391 | struct vme_resource *resource = NULL; | |
392 | ||
393 | bridge = dev_to_bridge(dev); | |
394 | if (bridge == NULL) { | |
395 | printk(KERN_ERR "Can't find VME bus\n"); | |
396 | goto err_bus; | |
397 | } | |
398 | ||
399 | /* Loop through master resources */ | |
400 | list_for_each(master_pos, &(bridge->master_resources)) { | |
401 | master_image = list_entry(master_pos, | |
402 | struct vme_master_resource, list); | |
403 | ||
404 | if (master_image == NULL) { | |
405 | printk(KERN_WARNING "Registered NULL master resource\n"); | |
406 | continue; | |
407 | } | |
408 | ||
409 | /* Find an unlocked and compatible image */ | |
410 | spin_lock(&(master_image->lock)); | |
411 | if(((master_image->address_attr & address) == address) && | |
412 | ((master_image->cycle_attr & cycle) == cycle) && | |
413 | ((master_image->width_attr & dwidth) == dwidth) && | |
414 | (master_image->locked == 0)) { | |
415 | ||
416 | master_image->locked = 1; | |
417 | spin_unlock(&(master_image->lock)); | |
418 | allocated_image = master_image; | |
419 | break; | |
420 | } | |
421 | spin_unlock(&(master_image->lock)); | |
422 | } | |
423 | ||
424 | /* Check to see if we found a resource */ | |
425 | if (allocated_image == NULL) { | |
426 | printk(KERN_ERR "Can't find a suitable resource\n"); | |
427 | goto err_image; | |
428 | } | |
429 | ||
430 | resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); | |
431 | if (resource == NULL) { | |
432 | printk(KERN_ERR "Unable to allocate resource structure\n"); | |
433 | goto err_alloc; | |
434 | } | |
435 | resource->type = VME_MASTER; | |
436 | resource->entry = &(allocated_image->list); | |
437 | ||
438 | return resource; | |
439 | ||
440 | kfree(resource); | |
441 | err_alloc: | |
442 | /* Unlock image */ | |
443 | spin_lock(&(master_image->lock)); | |
444 | master_image->locked = 0; | |
445 | spin_unlock(&(master_image->lock)); | |
446 | err_image: | |
447 | err_bus: | |
448 | return NULL; | |
449 | } | |
450 | EXPORT_SYMBOL(vme_master_request); | |
451 | ||
452 | int vme_master_set (struct vme_resource *resource, int enabled, | |
453 | unsigned long long vme_base, unsigned long long size, | |
454 | vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth) | |
455 | { | |
456 | struct vme_bridge *bridge = find_bridge(resource); | |
457 | struct vme_master_resource *image; | |
458 | int retval; | |
459 | ||
460 | if (resource->type != VME_MASTER) { | |
461 | printk("Not a master resource\n"); | |
462 | return -EINVAL; | |
463 | } | |
464 | ||
465 | image = list_entry(resource->entry, struct vme_master_resource, list); | |
466 | ||
467 | if (bridge->master_set == NULL) { | |
468 | printk("vme_master_set not supported\n"); | |
469 | return -EINVAL; | |
470 | } | |
471 | ||
472 | if(!(((image->address_attr & aspace) == aspace) && | |
473 | ((image->cycle_attr & cycle) == cycle) && | |
474 | ((image->width_attr & dwidth) == dwidth))) { | |
475 | printk("Invalid attributes\n"); | |
476 | return -EINVAL; | |
477 | } | |
478 | ||
479 | retval = vme_check_window(aspace, vme_base, size); | |
480 | if(retval) | |
481 | return retval; | |
482 | ||
483 | return bridge->master_set(image, enabled, vme_base, size, aspace, | |
484 | cycle, dwidth); | |
485 | } | |
486 | EXPORT_SYMBOL(vme_master_set); | |
487 | ||
488 | int vme_master_get (struct vme_resource *resource, int *enabled, | |
489 | unsigned long long *vme_base, unsigned long long *size, | |
490 | vme_address_t *aspace, vme_cycle_t *cycle, vme_width_t *dwidth) | |
491 | { | |
492 | struct vme_bridge *bridge = find_bridge(resource); | |
493 | struct vme_master_resource *image; | |
494 | ||
495 | if (resource->type != VME_MASTER) { | |
496 | printk("Not a master resource\n"); | |
497 | return -EINVAL; | |
498 | } | |
499 | ||
500 | image = list_entry(resource->entry, struct vme_master_resource, list); | |
501 | ||
502 | if (bridge->master_set == NULL) { | |
503 | printk("vme_master_set not supported\n"); | |
504 | return -EINVAL; | |
505 | } | |
506 | ||
507 | return bridge->master_get(image, enabled, vme_base, size, aspace, | |
508 | cycle, dwidth); | |
509 | } | |
510 | EXPORT_SYMBOL(vme_master_get); | |
511 | ||
512 | /* | |
513 | * Read data out of VME space into a buffer. | |
514 | */ | |
515 | ssize_t vme_master_read (struct vme_resource *resource, void *buf, size_t count, | |
516 | loff_t offset) | |
517 | { | |
518 | struct vme_bridge *bridge = find_bridge(resource); | |
519 | struct vme_master_resource *image; | |
520 | size_t length; | |
521 | ||
522 | if (bridge->master_read == NULL) { | |
523 | printk("Reading from resource not supported\n"); | |
524 | return -EINVAL; | |
525 | } | |
526 | ||
527 | if (resource->type != VME_MASTER) { | |
528 | printk("Not a master resource\n"); | |
529 | return -EINVAL; | |
530 | } | |
531 | ||
532 | image = list_entry(resource->entry, struct vme_master_resource, list); | |
533 | ||
534 | length = vme_get_size(resource); | |
535 | ||
536 | if (offset > length) { | |
537 | printk("Invalid Offset\n"); | |
538 | return -EFAULT; | |
539 | } | |
540 | ||
541 | if ((offset + count) > length) | |
542 | count = length - offset; | |
543 | ||
544 | return bridge->master_read(image, buf, count, offset); | |
545 | ||
546 | } | |
547 | EXPORT_SYMBOL(vme_master_read); | |
548 | ||
549 | /* | |
550 | * Write data out to VME space from a buffer. | |
551 | */ | |
552 | ssize_t vme_master_write (struct vme_resource *resource, void *buf, | |
553 | size_t count, loff_t offset) | |
554 | { | |
555 | struct vme_bridge *bridge = find_bridge(resource); | |
556 | struct vme_master_resource *image; | |
557 | size_t length; | |
558 | ||
559 | if (bridge->master_write == NULL) { | |
560 | printk("Writing to resource not supported\n"); | |
561 | return -EINVAL; | |
562 | } | |
563 | ||
564 | if (resource->type != VME_MASTER) { | |
565 | printk("Not a master resource\n"); | |
566 | return -EINVAL; | |
567 | } | |
568 | ||
569 | image = list_entry(resource->entry, struct vme_master_resource, list); | |
570 | ||
571 | length = vme_get_size(resource); | |
572 | ||
573 | if (offset > length) { | |
574 | printk("Invalid Offset\n"); | |
575 | return -EFAULT; | |
576 | } | |
577 | ||
578 | if ((offset + count) > length) | |
579 | count = length - offset; | |
580 | ||
581 | return bridge->master_write(image, buf, count, offset); | |
582 | } | |
583 | EXPORT_SYMBOL(vme_master_write); | |
584 | ||
585 | /* | |
586 | * Perform RMW cycle to provided location. | |
587 | */ | |
588 | unsigned int vme_master_rmw (struct vme_resource *resource, unsigned int mask, | |
589 | unsigned int compare, unsigned int swap, loff_t offset) | |
590 | { | |
591 | struct vme_bridge *bridge = find_bridge(resource); | |
592 | struct vme_master_resource *image; | |
593 | ||
594 | if (bridge->master_rmw == NULL) { | |
595 | printk("Writing to resource not supported\n"); | |
596 | return -EINVAL; | |
597 | } | |
598 | ||
599 | if (resource->type != VME_MASTER) { | |
600 | printk("Not a master resource\n"); | |
601 | return -EINVAL; | |
602 | } | |
603 | ||
604 | image = list_entry(resource->entry, struct vme_master_resource, list); | |
605 | ||
606 | return bridge->master_rmw(image, mask, compare, swap, offset); | |
607 | } | |
608 | EXPORT_SYMBOL(vme_master_rmw); | |
609 | ||
610 | void vme_master_free(struct vme_resource *resource) | |
611 | { | |
612 | struct vme_master_resource *master_image; | |
613 | ||
614 | if (resource->type != VME_MASTER) { | |
615 | printk("Not a master resource\n"); | |
616 | return; | |
617 | } | |
618 | ||
619 | master_image = list_entry(resource->entry, struct vme_master_resource, | |
620 | list); | |
621 | if (master_image == NULL) { | |
622 | printk("Can't find master resource\n"); | |
623 | return; | |
624 | } | |
625 | ||
626 | /* Unlock image */ | |
627 | spin_lock(&(master_image->lock)); | |
628 | if (master_image->locked == 0) | |
629 | printk(KERN_ERR "Image is already free\n"); | |
630 | ||
631 | master_image->locked = 0; | |
632 | spin_unlock(&(master_image->lock)); | |
633 | ||
634 | /* Free up resource memory */ | |
635 | kfree(resource); | |
636 | } | |
637 | EXPORT_SYMBOL(vme_master_free); | |
638 | ||
639 | /* | |
640 | * Request a DMA controller with specific attributes, return some unique | |
641 | * identifier. | |
642 | */ | |
643 | struct vme_resource *vme_request_dma(struct device *dev) | |
644 | { | |
645 | struct vme_bridge *bridge; | |
646 | struct list_head *dma_pos = NULL; | |
647 | struct vme_dma_resource *allocated_ctrlr = NULL; | |
648 | struct vme_dma_resource *dma_ctrlr = NULL; | |
649 | struct vme_resource *resource = NULL; | |
650 | ||
651 | /* XXX Not checking resource attributes */ | |
652 | printk(KERN_ERR "No VME resource Attribute tests done\n"); | |
653 | ||
654 | bridge = dev_to_bridge(dev); | |
655 | if (bridge == NULL) { | |
656 | printk(KERN_ERR "Can't find VME bus\n"); | |
657 | goto err_bus; | |
658 | } | |
659 | ||
660 | /* Loop through DMA resources */ | |
661 | list_for_each(dma_pos, &(bridge->dma_resources)) { | |
662 | dma_ctrlr = list_entry(dma_pos, | |
663 | struct vme_dma_resource, list); | |
664 | ||
665 | if (dma_ctrlr == NULL) { | |
666 | printk("Registered NULL DMA resource\n"); | |
667 | continue; | |
668 | } | |
669 | ||
670 | /* Find an unlocked controller */ | |
671 | down(&(dma_ctrlr->sem)); | |
672 | if(dma_ctrlr->locked == 0) { | |
673 | dma_ctrlr->locked = 1; | |
674 | up(&(dma_ctrlr->sem)); | |
675 | allocated_ctrlr = dma_ctrlr; | |
676 | break; | |
677 | } | |
678 | up(&(dma_ctrlr->sem)); | |
679 | } | |
680 | ||
681 | /* Check to see if we found a resource */ | |
682 | if (allocated_ctrlr == NULL) | |
683 | goto err_ctrlr; | |
684 | ||
685 | resource = kmalloc(sizeof(struct vme_resource), GFP_KERNEL); | |
686 | if (resource == NULL) { | |
687 | printk(KERN_WARNING "Unable to allocate resource structure\n"); | |
688 | goto err_alloc; | |
689 | } | |
690 | resource->type = VME_DMA; | |
691 | resource->entry = &(allocated_ctrlr->list); | |
692 | ||
693 | return resource; | |
694 | ||
695 | err_alloc: | |
696 | /* Unlock image */ | |
697 | down(&(dma_ctrlr->sem)); | |
698 | dma_ctrlr->locked = 0; | |
699 | up(&(dma_ctrlr->sem)); | |
700 | err_ctrlr: | |
701 | err_bus: | |
702 | return NULL; | |
703 | } | |
704 | EXPORT_SYMBOL(vme_request_dma); | |
705 | ||
706 | /* | |
707 | * Start new list | |
708 | */ | |
709 | struct vme_dma_list *vme_new_dma_list(struct vme_resource *resource) | |
710 | { | |
711 | struct vme_dma_resource *ctrlr; | |
712 | struct vme_dma_list *dma_list; | |
713 | ||
714 | if (resource->type != VME_DMA) { | |
715 | printk("Not a DMA resource\n"); | |
716 | return NULL; | |
717 | } | |
718 | ||
719 | ctrlr = list_entry(resource->entry, struct vme_dma_resource, list); | |
720 | ||
721 | dma_list = (struct vme_dma_list *)kmalloc( | |
722 | sizeof(struct vme_dma_list), GFP_KERNEL); | |
723 | if(dma_list == NULL) { | |
724 | printk("Unable to allocate memory for new dma list\n"); | |
725 | return NULL; | |
726 | } | |
727 | INIT_LIST_HEAD(&(dma_list->entries)); | |
728 | dma_list->parent = ctrlr; | |
729 | init_MUTEX(&(dma_list->sem)); | |
730 | ||
731 | return dma_list; | |
732 | } | |
733 | EXPORT_SYMBOL(vme_new_dma_list); | |
734 | ||
735 | /* | |
736 | * Create "Pattern" type attributes | |
737 | */ | |
738 | struct vme_dma_attr *vme_dma_pattern_attribute(u32 pattern, | |
739 | vme_pattern_t type) | |
740 | { | |
741 | struct vme_dma_attr *attributes; | |
742 | struct vme_dma_pattern *pattern_attr; | |
743 | ||
744 | attributes = (struct vme_dma_attr *)kmalloc( | |
745 | sizeof(struct vme_dma_attr), GFP_KERNEL); | |
746 | if(attributes == NULL) { | |
747 | printk("Unable to allocate memory for attributes structure\n"); | |
748 | goto err_attr; | |
749 | } | |
750 | ||
751 | pattern_attr = (struct vme_dma_pattern *)kmalloc( | |
752 | sizeof(struct vme_dma_pattern), GFP_KERNEL); | |
753 | if(pattern_attr == NULL) { | |
754 | printk("Unable to allocate memory for pattern attributes\n"); | |
755 | goto err_pat; | |
756 | } | |
757 | ||
758 | attributes->type = VME_DMA_PATTERN; | |
759 | attributes->private = (void *)pattern_attr; | |
760 | ||
761 | pattern_attr->pattern = pattern; | |
762 | pattern_attr->type = type; | |
763 | ||
764 | return attributes; | |
765 | ||
766 | kfree(pattern_attr); | |
767 | err_pat: | |
768 | kfree(attributes); | |
769 | err_attr: | |
770 | return NULL; | |
771 | } | |
772 | EXPORT_SYMBOL(vme_dma_pattern_attribute); | |
773 | ||
774 | /* | |
775 | * Create "PCI" type attributes | |
776 | */ | |
777 | struct vme_dma_attr *vme_dma_pci_attribute(dma_addr_t address) | |
778 | { | |
779 | struct vme_dma_attr *attributes; | |
780 | struct vme_dma_pci *pci_attr; | |
781 | ||
782 | /* XXX Run some sanity checks here */ | |
783 | ||
784 | attributes = (struct vme_dma_attr *)kmalloc( | |
785 | sizeof(struct vme_dma_attr), GFP_KERNEL); | |
786 | if(attributes == NULL) { | |
787 | printk("Unable to allocate memory for attributes structure\n"); | |
788 | goto err_attr; | |
789 | } | |
790 | ||
791 | pci_attr = (struct vme_dma_pci *)kmalloc(sizeof(struct vme_dma_pci), | |
792 | GFP_KERNEL); | |
793 | if(pci_attr == NULL) { | |
794 | printk("Unable to allocate memory for pci attributes\n"); | |
795 | goto err_pci; | |
796 | } | |
797 | ||
798 | ||
799 | ||
800 | attributes->type = VME_DMA_PCI; | |
801 | attributes->private = (void *)pci_attr; | |
802 | ||
803 | pci_attr->address = address; | |
804 | ||
805 | return attributes; | |
806 | ||
807 | kfree(pci_attr); | |
808 | err_pci: | |
809 | kfree(attributes); | |
810 | err_attr: | |
811 | return NULL; | |
812 | } | |
813 | EXPORT_SYMBOL(vme_dma_pci_attribute); | |
814 | ||
815 | /* | |
816 | * Create "VME" type attributes | |
817 | */ | |
818 | struct vme_dma_attr *vme_dma_vme_attribute(unsigned long long address, | |
819 | vme_address_t aspace, vme_cycle_t cycle, vme_width_t dwidth) | |
820 | { | |
821 | struct vme_dma_attr *attributes; | |
822 | struct vme_dma_vme *vme_attr; | |
823 | ||
824 | /* XXX Run some sanity checks here */ | |
825 | ||
826 | attributes = (struct vme_dma_attr *)kmalloc( | |
827 | sizeof(struct vme_dma_attr), GFP_KERNEL); | |
828 | if(attributes == NULL) { | |
829 | printk("Unable to allocate memory for attributes structure\n"); | |
830 | goto err_attr; | |
831 | } | |
832 | ||
833 | vme_attr = (struct vme_dma_vme *)kmalloc(sizeof(struct vme_dma_vme), | |
834 | GFP_KERNEL); | |
835 | if(vme_attr == NULL) { | |
836 | printk("Unable to allocate memory for vme attributes\n"); | |
837 | goto err_vme; | |
838 | } | |
839 | ||
840 | attributes->type = VME_DMA_VME; | |
841 | attributes->private = (void *)vme_attr; | |
842 | ||
843 | vme_attr->address = address; | |
844 | vme_attr->aspace = aspace; | |
845 | vme_attr->cycle = cycle; | |
846 | vme_attr->dwidth = dwidth; | |
847 | ||
848 | return attributes; | |
849 | ||
850 | kfree(vme_attr); | |
851 | err_vme: | |
852 | kfree(attributes); | |
853 | err_attr: | |
854 | return NULL; | |
855 | } | |
856 | EXPORT_SYMBOL(vme_dma_vme_attribute); | |
857 | ||
858 | /* | |
859 | * Free attribute | |
860 | */ | |
861 | void vme_dma_free_attribute(struct vme_dma_attr *attributes) | |
862 | { | |
863 | kfree(attributes->private); | |
864 | kfree(attributes); | |
865 | } | |
866 | EXPORT_SYMBOL(vme_dma_free_attribute); | |
867 | ||
868 | int vme_dma_list_add(struct vme_dma_list *list, struct vme_dma_attr *src, | |
869 | struct vme_dma_attr *dest, size_t count) | |
870 | { | |
871 | struct vme_bridge *bridge = list->parent->parent; | |
872 | int retval; | |
873 | ||
874 | if (bridge->dma_list_add == NULL) { | |
875 | printk("Link List DMA generation not supported\n"); | |
876 | return -EINVAL; | |
877 | } | |
878 | ||
879 | if (down_trylock(&(list->sem))) { | |
880 | printk("Link List already submitted\n"); | |
881 | return -EINVAL; | |
882 | } | |
883 | ||
884 | retval = bridge->dma_list_add(list, src, dest, count); | |
885 | ||
886 | up(&(list->sem)); | |
887 | ||
888 | return retval; | |
889 | } | |
890 | EXPORT_SYMBOL(vme_dma_list_add); | |
891 | ||
892 | int vme_dma_list_exec(struct vme_dma_list *list) | |
893 | { | |
894 | struct vme_bridge *bridge = list->parent->parent; | |
895 | int retval; | |
896 | ||
897 | if (bridge->dma_list_exec == NULL) { | |
898 | printk("Link List DMA execution not supported\n"); | |
899 | return -EINVAL; | |
900 | } | |
901 | ||
902 | down(&(list->sem)); | |
903 | ||
904 | retval = bridge->dma_list_exec(list); | |
905 | ||
906 | up(&(list->sem)); | |
907 | ||
908 | return retval; | |
909 | } | |
910 | EXPORT_SYMBOL(vme_dma_list_exec); | |
911 | ||
912 | int vme_dma_list_free(struct vme_dma_list *list) | |
913 | { | |
914 | struct vme_bridge *bridge = list->parent->parent; | |
915 | int retval; | |
916 | ||
917 | if (bridge->dma_list_empty == NULL) { | |
918 | printk("Emptying of Link Lists not supported\n"); | |
919 | return -EINVAL; | |
920 | } | |
921 | ||
922 | if (down_trylock(&(list->sem))) { | |
923 | printk("Link List in use\n"); | |
924 | return -EINVAL; | |
925 | } | |
926 | ||
927 | /* | |
928 | * Empty out all of the entries from the dma list. We need to go to the | |
929 | * low level driver as dma entries are driver specific. | |
930 | */ | |
931 | retval = bridge->dma_list_empty(list); | |
932 | if (retval) { | |
933 | printk("Unable to empty link-list entries\n"); | |
934 | up(&(list->sem)); | |
935 | return retval; | |
936 | } | |
937 | up(&(list->sem)); | |
938 | kfree(list); | |
939 | ||
940 | return retval; | |
941 | } | |
942 | EXPORT_SYMBOL(vme_dma_list_free); | |
943 | ||
944 | int vme_dma_free(struct vme_resource *resource) | |
945 | { | |
946 | struct vme_dma_resource *ctrlr; | |
947 | ||
948 | if (resource->type != VME_DMA) { | |
949 | printk("Not a DMA resource\n"); | |
950 | return -EINVAL; | |
951 | } | |
952 | ||
953 | ctrlr = list_entry(resource->entry, struct vme_dma_resource, list); | |
954 | ||
955 | if (down_trylock(&(ctrlr->sem))) { | |
956 | printk("Resource busy, can't free\n"); | |
957 | return -EBUSY; | |
958 | } | |
959 | ||
960 | if (!(list_empty(&(ctrlr->pending)) && list_empty(&(ctrlr->running)))) { | |
961 | printk("Resource still processing transfers\n"); | |
962 | up(&(ctrlr->sem)); | |
963 | return -EBUSY; | |
964 | } | |
965 | ||
966 | ctrlr->locked = 0; | |
967 | ||
968 | up(&(ctrlr->sem)); | |
969 | ||
970 | return 0; | |
971 | } | |
972 | EXPORT_SYMBOL(vme_dma_free); | |
973 | ||
974 | int vme_request_irq(struct device *dev, int level, int statid, | |
975 | void (*callback)(int level, int vector, void *priv_data), | |
976 | void *priv_data) | |
977 | { | |
978 | struct vme_bridge *bridge; | |
979 | ||
980 | bridge = dev_to_bridge(dev); | |
981 | if (bridge == NULL) { | |
982 | printk(KERN_ERR "Can't find VME bus\n"); | |
983 | return -EINVAL; | |
984 | } | |
985 | ||
986 | if((level < 1) || (level > 7)) { | |
987 | printk(KERN_WARNING "Invalid interrupt level\n"); | |
988 | return -EINVAL; | |
989 | } | |
990 | ||
991 | if (bridge->request_irq == NULL) { | |
992 | printk("Registering interrupts not supported\n"); | |
993 | return -EINVAL; | |
994 | } | |
995 | ||
996 | return bridge->request_irq(level, statid, callback, priv_data); | |
997 | } | |
998 | EXPORT_SYMBOL(vme_request_irq); | |
999 | ||
1000 | void vme_free_irq(struct device *dev, int level, int statid) | |
1001 | { | |
1002 | struct vme_bridge *bridge; | |
1003 | ||
1004 | bridge = dev_to_bridge(dev); | |
1005 | if (bridge == NULL) { | |
1006 | printk(KERN_ERR "Can't find VME bus\n"); | |
1007 | return; | |
1008 | } | |
1009 | ||
1010 | if((level < 1) || (level > 7)) { | |
1011 | printk(KERN_WARNING "Invalid interrupt level\n"); | |
1012 | return; | |
1013 | } | |
1014 | ||
1015 | if (bridge->free_irq == NULL) { | |
1016 | printk("Freeing interrupts not supported\n"); | |
1017 | return; | |
1018 | } | |
1019 | ||
1020 | bridge->free_irq(level, statid); | |
1021 | } | |
1022 | EXPORT_SYMBOL(vme_free_irq); | |
1023 | ||
1024 | int vme_generate_irq(struct device *dev, int level, int statid) | |
1025 | { | |
1026 | struct vme_bridge *bridge; | |
1027 | ||
1028 | bridge = dev_to_bridge(dev); | |
1029 | if (bridge == NULL) { | |
1030 | printk(KERN_ERR "Can't find VME bus\n"); | |
1031 | return -EINVAL; | |
1032 | } | |
1033 | ||
1034 | if((level < 1) || (level > 7)) { | |
1035 | printk(KERN_WARNING "Invalid interrupt level\n"); | |
1036 | return -EINVAL; | |
1037 | } | |
1038 | ||
1039 | if (bridge->generate_irq == NULL) { | |
1040 | printk("Interrupt generation not supported\n"); | |
1041 | return -EINVAL; | |
1042 | } | |
1043 | ||
1044 | return bridge->generate_irq(level, statid); | |
1045 | } | |
1046 | EXPORT_SYMBOL(vme_generate_irq); | |
1047 | ||
1048 | int vme_lm_set(struct device *dev, unsigned long long lm_base, vme_address_t aspace, | |
1049 | vme_cycle_t cycle) | |
1050 | { | |
1051 | struct vme_bridge *bridge; | |
1052 | ||
1053 | bridge = dev_to_bridge(dev); | |
1054 | if (bridge == NULL) { | |
1055 | printk(KERN_ERR "Can't find VME bus\n"); | |
1056 | return -EINVAL; | |
1057 | } | |
1058 | ||
1059 | if (bridge->lm_set == NULL) { | |
1060 | printk("vme_lm_set not supported\n"); | |
1061 | return -EINVAL; | |
1062 | } | |
1063 | ||
1064 | return bridge->lm_set(lm_base, aspace, cycle); | |
1065 | } | |
1066 | EXPORT_SYMBOL(vme_lm_set); | |
1067 | ||
1068 | int vme_lm_get(struct device *dev, unsigned long long *lm_base, vme_address_t *aspace, | |
1069 | vme_cycle_t *cycle) | |
1070 | { | |
1071 | struct vme_bridge *bridge; | |
1072 | ||
1073 | bridge = dev_to_bridge(dev); | |
1074 | if (bridge == NULL) { | |
1075 | printk(KERN_ERR "Can't find VME bus\n"); | |
1076 | return -EINVAL; | |
1077 | } | |
1078 | ||
1079 | if (bridge->lm_get == NULL) { | |
1080 | printk("vme_lm_get not supported\n"); | |
1081 | return -EINVAL; | |
1082 | } | |
1083 | ||
1084 | return bridge->lm_get(lm_base, aspace, cycle); | |
1085 | } | |
1086 | EXPORT_SYMBOL(vme_lm_get); | |
1087 | ||
1088 | int vme_lm_attach(struct device *dev, int monitor, void (*callback)(int)) | |
1089 | { | |
1090 | struct vme_bridge *bridge; | |
1091 | ||
1092 | bridge = dev_to_bridge(dev); | |
1093 | if (bridge == NULL) { | |
1094 | printk(KERN_ERR "Can't find VME bus\n"); | |
1095 | return -EINVAL; | |
1096 | } | |
1097 | ||
1098 | if (bridge->lm_attach == NULL) { | |
1099 | printk("vme_lm_attach not supported\n"); | |
1100 | return -EINVAL; | |
1101 | } | |
1102 | ||
1103 | return bridge->lm_attach(monitor, callback); | |
1104 | } | |
1105 | EXPORT_SYMBOL(vme_lm_attach); | |
1106 | ||
1107 | int vme_lm_detach(struct device *dev, int monitor) | |
1108 | { | |
1109 | struct vme_bridge *bridge; | |
1110 | ||
1111 | bridge = dev_to_bridge(dev); | |
1112 | if (bridge == NULL) { | |
1113 | printk(KERN_ERR "Can't find VME bus\n"); | |
1114 | return -EINVAL; | |
1115 | } | |
1116 | ||
1117 | if (bridge->lm_detach == NULL) { | |
1118 | printk("vme_lm_detach not supported\n"); | |
1119 | return -EINVAL; | |
1120 | } | |
1121 | ||
1122 | return bridge->lm_detach(monitor); | |
1123 | } | |
1124 | EXPORT_SYMBOL(vme_lm_detach); | |
1125 | ||
1126 | int vme_slot_get(struct device *bus) | |
1127 | { | |
1128 | struct vme_bridge *bridge; | |
1129 | ||
1130 | bridge = dev_to_bridge(bus); | |
1131 | if (bridge == NULL) { | |
1132 | printk(KERN_ERR "Can't find VME bus\n"); | |
1133 | return -EINVAL; | |
1134 | } | |
1135 | ||
1136 | if (bridge->slot_get == NULL) { | |
1137 | printk("vme_slot_get not supported\n"); | |
1138 | return -EINVAL; | |
1139 | } | |
1140 | ||
1141 | return bridge->slot_get(); | |
1142 | } | |
1143 | EXPORT_SYMBOL(vme_slot_get); | |
1144 | ||
1145 | ||
1146 | /* - Bridge Registration --------------------------------------------------- */ | |
1147 | ||
1148 | static int vme_alloc_bus_num(void) | |
1149 | { | |
1150 | int i; | |
1151 | ||
1152 | down(&vme_bus_num_sem); | |
1153 | for (i = 0; i < sizeof(vme_bus_numbers) * 8; i++) { | |
1154 | if (((vme_bus_numbers >> i) & 0x1) == 0) { | |
1155 | vme_bus_numbers |= (0x1 << i); | |
1156 | break; | |
1157 | } | |
1158 | } | |
1159 | up(&vme_bus_num_sem); | |
1160 | ||
1161 | return i; | |
1162 | } | |
1163 | ||
1164 | static void vme_free_bus_num(int bus) | |
1165 | { | |
1166 | down(&vme_bus_num_sem); | |
1167 | vme_bus_numbers |= ~(0x1 << bus); | |
1168 | up(&vme_bus_num_sem); | |
1169 | } | |
1170 | ||
1171 | int vme_register_bridge (struct vme_bridge *bridge) | |
1172 | { | |
1173 | struct device *dev; | |
1174 | int retval; | |
1175 | int i; | |
1176 | ||
1177 | bridge->num = vme_alloc_bus_num(); | |
1178 | ||
1179 | /* This creates 32 vme "slot" devices. This equates to a slot for each | |
1180 | * ID available in a system conforming to the ANSI/VITA 1-1994 | |
1181 | * specification. | |
1182 | */ | |
1183 | for (i = 0; i < VME_SLOTS_MAX; i++) { | |
1184 | dev = &(bridge->dev[i]); | |
1185 | memset(dev, 0, sizeof(struct device)); | |
1186 | ||
1187 | dev->parent = bridge->parent; | |
1188 | dev->bus = &(vme_bus_type); | |
1189 | /* | |
1190 | * We save a pointer to the bridge in platform_data so that we | |
1191 | * can get to it later. We keep driver_data for use by the | |
1192 | * driver that binds against the slot | |
1193 | */ | |
1194 | dev->platform_data = bridge; | |
1195 | dev_set_name(dev, "vme-%x.%x", bridge->num, i + 1); | |
1196 | ||
1197 | retval = device_register(dev); | |
1198 | if(retval) | |
1199 | goto err_reg; | |
1200 | } | |
1201 | ||
1202 | return retval; | |
1203 | ||
1204 | i = VME_SLOTS_MAX; | |
1205 | err_reg: | |
1206 | while (i > -1) { | |
1207 | dev = &(bridge->dev[i]); | |
1208 | device_unregister(dev); | |
1209 | } | |
1210 | vme_free_bus_num(bridge->num); | |
1211 | return retval; | |
1212 | } | |
1213 | EXPORT_SYMBOL(vme_register_bridge); | |
1214 | ||
1215 | void vme_unregister_bridge (struct vme_bridge *bridge) | |
1216 | { | |
1217 | int i; | |
1218 | struct device *dev; | |
1219 | ||
1220 | ||
1221 | for (i = 0; i < VME_SLOTS_MAX; i++) { | |
1222 | dev = &(bridge->dev[i]); | |
1223 | device_unregister(dev); | |
1224 | } | |
1225 | vme_free_bus_num(bridge->num); | |
1226 | } | |
1227 | EXPORT_SYMBOL(vme_unregister_bridge); | |
1228 | ||
1229 | ||
1230 | /* - Driver Registration --------------------------------------------------- */ | |
1231 | ||
1232 | int vme_register_driver (struct vme_driver *drv) | |
1233 | { | |
1234 | drv->driver.name = drv->name; | |
1235 | drv->driver.bus = &vme_bus_type; | |
1236 | ||
1237 | return driver_register(&drv->driver); | |
1238 | } | |
1239 | EXPORT_SYMBOL(vme_register_driver); | |
1240 | ||
1241 | void vme_unregister_driver (struct vme_driver *drv) | |
1242 | { | |
1243 | driver_unregister(&drv->driver); | |
1244 | } | |
1245 | EXPORT_SYMBOL(vme_unregister_driver); | |
1246 | ||
1247 | /* - Bus Registration ------------------------------------------------------ */ | |
1248 | ||
1249 | int vme_calc_slot(struct device *dev) | |
1250 | { | |
1251 | struct vme_bridge *bridge; | |
1252 | int num; | |
1253 | ||
1254 | bridge = dev_to_bridge(dev); | |
1255 | ||
1256 | /* Determine slot number */ | |
1257 | num = 0; | |
1258 | while(num < VME_SLOTS_MAX) { | |
1259 | if(&(bridge->dev[num]) == dev) { | |
1260 | break; | |
1261 | } | |
1262 | num++; | |
1263 | } | |
1264 | if (num == VME_SLOTS_MAX) { | |
1265 | dev_err(dev, "Failed to identify slot\n"); | |
1266 | num = 0; | |
1267 | goto err_dev; | |
1268 | } | |
1269 | num++; | |
1270 | ||
1271 | err_dev: | |
1272 | return num; | |
1273 | } | |
1274 | ||
1275 | static struct vme_driver *dev_to_vme_driver(struct device *dev) | |
1276 | { | |
1277 | if(dev->driver == NULL) | |
1278 | printk("Bugger dev->driver is NULL\n"); | |
1279 | ||
1280 | return container_of(dev->driver, struct vme_driver, driver); | |
1281 | } | |
1282 | ||
1283 | static int vme_bus_match(struct device *dev, struct device_driver *drv) | |
1284 | { | |
1285 | struct vme_bridge *bridge; | |
1286 | struct vme_driver *driver; | |
1287 | int i, num; | |
1288 | ||
1289 | bridge = dev_to_bridge(dev); | |
1290 | driver = container_of(drv, struct vme_driver, driver); | |
1291 | ||
1292 | num = vme_calc_slot(dev); | |
1293 | if (!num) | |
1294 | goto err_dev; | |
1295 | ||
1296 | if (driver->bind_table == NULL) { | |
1297 | dev_err(dev, "Bind table NULL\n"); | |
1298 | goto err_table; | |
1299 | } | |
1300 | ||
1301 | i = 0; | |
1302 | while((driver->bind_table[i].bus != 0) || | |
1303 | (driver->bind_table[i].slot != 0)) { | |
1304 | ||
1305 | if ((bridge->num == driver->bind_table[i].bus) && | |
1306 | (num == driver->bind_table[i].slot)) | |
1307 | return 1; | |
1308 | i++; | |
1309 | } | |
1310 | ||
1311 | err_dev: | |
1312 | err_table: | |
1313 | return 0; | |
1314 | } | |
1315 | ||
1316 | static int vme_bus_probe(struct device *dev) | |
1317 | { | |
1318 | struct vme_bridge *bridge; | |
1319 | struct vme_driver *driver; | |
1320 | int retval = -ENODEV; | |
1321 | ||
1322 | driver = dev_to_vme_driver(dev); | |
1323 | bridge = dev_to_bridge(dev); | |
1324 | ||
1325 | if(driver->probe != NULL) { | |
1326 | retval = driver->probe(dev, bridge->num, vme_calc_slot(dev)); | |
1327 | } | |
1328 | ||
1329 | return retval; | |
1330 | } | |
1331 | ||
1332 | static int vme_bus_remove(struct device *dev) | |
1333 | { | |
1334 | struct vme_bridge *bridge; | |
1335 | struct vme_driver *driver; | |
1336 | int retval = -ENODEV; | |
1337 | ||
1338 | driver = dev_to_vme_driver(dev); | |
1339 | bridge = dev_to_bridge(dev); | |
1340 | ||
1341 | if(driver->remove != NULL) { | |
1342 | retval = driver->remove(dev, bridge->num, vme_calc_slot(dev)); | |
1343 | } | |
1344 | ||
1345 | return retval; | |
1346 | } | |
1347 | ||
1348 | struct bus_type vme_bus_type = { | |
1349 | .name = "vme", | |
1350 | .match = vme_bus_match, | |
1351 | .probe = vme_bus_probe, | |
1352 | .remove = vme_bus_remove, | |
1353 | }; | |
1354 | EXPORT_SYMBOL(vme_bus_type); | |
1355 | ||
1356 | static int __init vme_init (void) | |
1357 | { | |
1358 | return bus_register(&vme_bus_type); | |
1359 | } | |
1360 | ||
1361 | static void __exit vme_exit (void) | |
1362 | { | |
1363 | bus_unregister(&vme_bus_type); | |
1364 | } | |
1365 | ||
1366 | MODULE_DESCRIPTION("VME bridge driver framework"); | |
1367 | MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com"); | |
1368 | MODULE_LICENSE("GPL"); | |
1369 | ||
1370 | module_init(vme_init); | |
1371 | module_exit(vme_exit); |