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