1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Fake VME bridge support. |
4 | * |
5 | * This drive provides a fake VME bridge chip, this enables debugging of the |
6 | * VME framework in the absence of a VME system. |
7 | * |
8 | * This driver has to do a number of things in software that would be driven |
9 | * by hardware if it was available, it will also result in extra overhead at |
10 | * times when compared with driving actual hardware. |
11 | * |
12 | * Author: Martyn Welch <martyn@welches.me.uk> |
13 | * Copyright (c) 2014 Martyn Welch |
14 | * |
15 | * Based on vme_tsi148.c: |
16 | * |
17 | * Author: Martyn Welch <martyn.welch@ge.com> |
18 | * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. |
19 | * |
20 | * Based on work by Tom Armistead and Ajit Prem |
21 | * Copyright 2004 Motorola Inc. |
22 | */ |
23 | |
24 | #include <linux/device.h> |
25 | #include <linux/errno.h> |
26 | #include <linux/interrupt.h> |
27 | #include <linux/module.h> |
28 | #include <linux/moduleparam.h> |
29 | #include <linux/slab.h> |
30 | #include <linux/spinlock.h> |
31 | #include <linux/types.h> |
32 | |
33 | #include "vme.h" |
34 | #include "vme_bridge.h" |
35 | |
36 | /* |
37 | * Define the number of each that the fake driver supports. |
38 | */ |
39 | #define FAKE_MAX_MASTER 8 /* Max Master Windows */ |
40 | #define FAKE_MAX_SLAVE 8 /* Max Slave Windows */ |
41 | |
42 | /* Structures to hold information normally held in device registers */ |
43 | struct fake_slave_window { |
44 | int enabled; |
45 | unsigned long long vme_base; |
46 | unsigned long long size; |
47 | void *buf_base; |
48 | u32 aspace; |
49 | u32 cycle; |
50 | }; |
51 | |
52 | struct fake_master_window { |
53 | int enabled; |
54 | unsigned long long vme_base; |
55 | unsigned long long size; |
56 | u32 aspace; |
57 | u32 cycle; |
58 | u32 dwidth; |
59 | }; |
60 | |
61 | /* Structure used to hold driver specific information */ |
62 | struct fake_driver { |
63 | struct vme_bridge *parent; |
64 | struct fake_slave_window slaves[FAKE_MAX_SLAVE]; |
65 | struct fake_master_window masters[FAKE_MAX_MASTER]; |
66 | u32 lm_enabled; |
67 | unsigned long long lm_base; |
68 | u32 lm_aspace; |
69 | u32 lm_cycle; |
70 | void (*lm_callback[4])(void *); |
71 | void *lm_data[4]; |
72 | struct tasklet_struct int_tasklet; |
73 | int int_level; |
74 | int int_statid; |
75 | void *crcsr_kernel; |
76 | dma_addr_t crcsr_bus; |
77 | /* Only one VME interrupt can be generated at a time, provide locking */ |
78 | struct mutex vme_int; |
79 | }; |
80 | |
81 | /* Module parameter */ |
82 | static int geoid; |
83 | |
84 | static const char driver_name[] = "vme_fake" ; |
85 | |
86 | static struct vme_bridge *exit_pointer; |
87 | |
88 | static struct device *vme_root; |
89 | |
90 | /* |
91 | * Calling VME bus interrupt callback if provided. |
92 | */ |
93 | static void fake_VIRQ_tasklet(unsigned long data) |
94 | { |
95 | struct vme_bridge *fake_bridge; |
96 | struct fake_driver *bridge; |
97 | |
98 | fake_bridge = (struct vme_bridge *)data; |
99 | bridge = fake_bridge->driver_priv; |
100 | |
101 | vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid); |
102 | } |
103 | |
104 | /* |
105 | * Configure VME interrupt |
106 | */ |
107 | static void fake_irq_set(struct vme_bridge *fake_bridge, int level, |
108 | int state, int sync) |
109 | { |
110 | /* Nothing to do */ |
111 | } |
112 | |
113 | static void *fake_pci_to_ptr(dma_addr_t addr) |
114 | { |
115 | return (void *)(uintptr_t)addr; |
116 | } |
117 | |
118 | static dma_addr_t fake_ptr_to_pci(void *addr) |
119 | { |
120 | return (dma_addr_t)(uintptr_t)addr; |
121 | } |
122 | |
123 | /* |
124 | * Generate a VME bus interrupt at the requested level & vector. Wait for |
125 | * interrupt to be acked. |
126 | */ |
127 | static int fake_irq_generate(struct vme_bridge *fake_bridge, int level, |
128 | int statid) |
129 | { |
130 | struct fake_driver *bridge; |
131 | |
132 | bridge = fake_bridge->driver_priv; |
133 | |
134 | mutex_lock(&bridge->vme_int); |
135 | |
136 | bridge->int_level = level; |
137 | |
138 | bridge->int_statid = statid; |
139 | |
140 | /* |
141 | * Schedule tasklet to run VME handler to emulate normal VME interrupt |
142 | * handler behaviour. |
143 | */ |
144 | tasklet_schedule(t: &bridge->int_tasklet); |
145 | |
146 | mutex_unlock(lock: &bridge->vme_int); |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | /* |
152 | * Initialize a slave window with the requested attributes. |
153 | */ |
154 | static int fake_slave_set(struct vme_slave_resource *image, int enabled, |
155 | unsigned long long vme_base, unsigned long long size, |
156 | dma_addr_t buf_base, u32 aspace, u32 cycle) |
157 | { |
158 | unsigned int i, granularity = 0; |
159 | unsigned long long vme_bound; |
160 | struct vme_bridge *fake_bridge; |
161 | struct fake_driver *bridge; |
162 | |
163 | fake_bridge = image->parent; |
164 | bridge = fake_bridge->driver_priv; |
165 | |
166 | i = image->number; |
167 | |
168 | switch (aspace) { |
169 | case VME_A16: |
170 | granularity = 0x10; |
171 | break; |
172 | case VME_A24: |
173 | granularity = 0x1000; |
174 | break; |
175 | case VME_A32: |
176 | granularity = 0x10000; |
177 | break; |
178 | case VME_A64: |
179 | granularity = 0x10000; |
180 | break; |
181 | case VME_CRCSR: |
182 | case VME_USER1: |
183 | case VME_USER2: |
184 | case VME_USER3: |
185 | case VME_USER4: |
186 | default: |
187 | pr_err("Invalid address space\n" ); |
188 | return -EINVAL; |
189 | } |
190 | |
191 | /* |
192 | * Bound address is a valid address for the window, adjust |
193 | * accordingly |
194 | */ |
195 | vme_bound = vme_base + size - granularity; |
196 | |
197 | if (vme_base & (granularity - 1)) { |
198 | pr_err("Invalid VME base alignment\n" ); |
199 | return -EINVAL; |
200 | } |
201 | if (vme_bound & (granularity - 1)) { |
202 | pr_err("Invalid VME bound alignment\n" ); |
203 | return -EINVAL; |
204 | } |
205 | |
206 | mutex_lock(&image->mtx); |
207 | |
208 | bridge->slaves[i].enabled = enabled; |
209 | bridge->slaves[i].vme_base = vme_base; |
210 | bridge->slaves[i].size = size; |
211 | bridge->slaves[i].buf_base = fake_pci_to_ptr(addr: buf_base); |
212 | bridge->slaves[i].aspace = aspace; |
213 | bridge->slaves[i].cycle = cycle; |
214 | |
215 | mutex_unlock(lock: &image->mtx); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | /* |
221 | * Get slave window configuration. |
222 | */ |
223 | static int fake_slave_get(struct vme_slave_resource *image, int *enabled, |
224 | unsigned long long *vme_base, unsigned long long *size, |
225 | dma_addr_t *buf_base, u32 *aspace, u32 *cycle) |
226 | { |
227 | unsigned int i; |
228 | struct fake_driver *bridge; |
229 | |
230 | bridge = image->parent->driver_priv; |
231 | |
232 | i = image->number; |
233 | |
234 | mutex_lock(&image->mtx); |
235 | |
236 | *enabled = bridge->slaves[i].enabled; |
237 | *vme_base = bridge->slaves[i].vme_base; |
238 | *size = bridge->slaves[i].size; |
239 | *buf_base = fake_ptr_to_pci(addr: bridge->slaves[i].buf_base); |
240 | *aspace = bridge->slaves[i].aspace; |
241 | *cycle = bridge->slaves[i].cycle; |
242 | |
243 | mutex_unlock(lock: &image->mtx); |
244 | |
245 | return 0; |
246 | } |
247 | |
248 | /* |
249 | * Set the attributes of an outbound window. |
250 | */ |
251 | static int fake_master_set(struct vme_master_resource *image, int enabled, |
252 | unsigned long long vme_base, unsigned long long size, |
253 | u32 aspace, u32 cycle, u32 dwidth) |
254 | { |
255 | int retval = 0; |
256 | unsigned int i; |
257 | struct vme_bridge *fake_bridge; |
258 | struct fake_driver *bridge; |
259 | |
260 | fake_bridge = image->parent; |
261 | |
262 | bridge = fake_bridge->driver_priv; |
263 | |
264 | /* Verify input data */ |
265 | if (vme_base & 0xFFFF) { |
266 | pr_err("Invalid VME Window alignment\n" ); |
267 | retval = -EINVAL; |
268 | goto err_window; |
269 | } |
270 | |
271 | if (size & 0xFFFF) { |
272 | pr_err("Invalid size alignment\n" ); |
273 | retval = -EINVAL; |
274 | goto err_window; |
275 | } |
276 | |
277 | if ((size == 0) && (enabled != 0)) { |
278 | pr_err("Size must be non-zero for enabled windows\n" ); |
279 | retval = -EINVAL; |
280 | goto err_window; |
281 | } |
282 | |
283 | /* Setup data width */ |
284 | switch (dwidth) { |
285 | case VME_D8: |
286 | case VME_D16: |
287 | case VME_D32: |
288 | break; |
289 | default: |
290 | pr_err("Invalid data width\n" ); |
291 | retval = -EINVAL; |
292 | goto err_dwidth; |
293 | } |
294 | |
295 | /* Setup address space */ |
296 | switch (aspace) { |
297 | case VME_A16: |
298 | case VME_A24: |
299 | case VME_A32: |
300 | case VME_A64: |
301 | case VME_CRCSR: |
302 | case VME_USER1: |
303 | case VME_USER2: |
304 | case VME_USER3: |
305 | case VME_USER4: |
306 | break; |
307 | default: |
308 | pr_err("Invalid address space\n" ); |
309 | retval = -EINVAL; |
310 | goto err_aspace; |
311 | } |
312 | |
313 | spin_lock(lock: &image->lock); |
314 | |
315 | i = image->number; |
316 | |
317 | bridge->masters[i].enabled = enabled; |
318 | bridge->masters[i].vme_base = vme_base; |
319 | bridge->masters[i].size = size; |
320 | bridge->masters[i].aspace = aspace; |
321 | bridge->masters[i].cycle = cycle; |
322 | bridge->masters[i].dwidth = dwidth; |
323 | |
324 | spin_unlock(lock: &image->lock); |
325 | |
326 | return 0; |
327 | |
328 | err_aspace: |
329 | err_dwidth: |
330 | err_window: |
331 | return retval; |
332 | } |
333 | |
334 | /* |
335 | * Set the attributes of an outbound window. |
336 | */ |
337 | static int __fake_master_get(struct vme_master_resource *image, int *enabled, |
338 | unsigned long long *vme_base, unsigned long long *size, |
339 | u32 *aspace, u32 *cycle, u32 *dwidth) |
340 | { |
341 | unsigned int i; |
342 | struct fake_driver *bridge; |
343 | |
344 | bridge = image->parent->driver_priv; |
345 | |
346 | i = image->number; |
347 | |
348 | *enabled = bridge->masters[i].enabled; |
349 | *vme_base = bridge->masters[i].vme_base; |
350 | *size = bridge->masters[i].size; |
351 | *aspace = bridge->masters[i].aspace; |
352 | *cycle = bridge->masters[i].cycle; |
353 | *dwidth = bridge->masters[i].dwidth; |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static int fake_master_get(struct vme_master_resource *image, int *enabled, |
359 | unsigned long long *vme_base, unsigned long long *size, |
360 | u32 *aspace, u32 *cycle, u32 *dwidth) |
361 | { |
362 | int retval; |
363 | |
364 | spin_lock(lock: &image->lock); |
365 | |
366 | retval = __fake_master_get(image, enabled, vme_base, size, aspace, |
367 | cycle, dwidth); |
368 | |
369 | spin_unlock(lock: &image->lock); |
370 | |
371 | return retval; |
372 | } |
373 | |
374 | static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr, |
375 | u32 aspace, u32 cycle) |
376 | { |
377 | struct vme_bridge *fake_bridge; |
378 | unsigned long long lm_base; |
379 | u32 lm_aspace, lm_cycle; |
380 | int i; |
381 | struct vme_lm_resource *lm; |
382 | struct list_head *pos = NULL, *n; |
383 | |
384 | /* Get vme_bridge */ |
385 | fake_bridge = bridge->parent; |
386 | |
387 | /* Loop through each location monitor resource */ |
388 | list_for_each_safe(pos, n, &fake_bridge->lm_resources) { |
389 | lm = list_entry(pos, struct vme_lm_resource, list); |
390 | |
391 | /* If disabled, we're done */ |
392 | if (bridge->lm_enabled == 0) |
393 | return; |
394 | |
395 | lm_base = bridge->lm_base; |
396 | lm_aspace = bridge->lm_aspace; |
397 | lm_cycle = bridge->lm_cycle; |
398 | |
399 | /* First make sure that the cycle and address space match */ |
400 | if ((lm_aspace == aspace) && (lm_cycle == cycle)) { |
401 | for (i = 0; i < lm->monitors; i++) { |
402 | /* Each location monitor covers 8 bytes */ |
403 | if (((lm_base + (8 * i)) <= addr) && |
404 | ((lm_base + (8 * i) + 8) > addr)) { |
405 | if (bridge->lm_callback[i]) |
406 | bridge->lm_callback[i](bridge->lm_data[i]); |
407 | } |
408 | } |
409 | } |
410 | } |
411 | } |
412 | |
413 | static noinline_for_stack u8 fake_vmeread8(struct fake_driver *bridge, |
414 | unsigned long long addr, |
415 | u32 aspace, u32 cycle) |
416 | { |
417 | u8 retval = 0xff; |
418 | int i; |
419 | unsigned long long start, end, offset; |
420 | u8 *loc; |
421 | |
422 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
423 | start = bridge->slaves[i].vme_base; |
424 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
425 | |
426 | if (aspace != bridge->slaves[i].aspace) |
427 | continue; |
428 | |
429 | if (cycle != bridge->slaves[i].cycle) |
430 | continue; |
431 | |
432 | if ((addr >= start) && (addr < end)) { |
433 | offset = addr - bridge->slaves[i].vme_base; |
434 | loc = (u8 *)(bridge->slaves[i].buf_base + offset); |
435 | retval = *loc; |
436 | |
437 | break; |
438 | } |
439 | } |
440 | |
441 | fake_lm_check(bridge, addr, aspace, cycle); |
442 | |
443 | return retval; |
444 | } |
445 | |
446 | static noinline_for_stack u16 fake_vmeread16(struct fake_driver *bridge, |
447 | unsigned long long addr, |
448 | u32 aspace, u32 cycle) |
449 | { |
450 | u16 retval = 0xffff; |
451 | int i; |
452 | unsigned long long start, end, offset; |
453 | u16 *loc; |
454 | |
455 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
456 | if (aspace != bridge->slaves[i].aspace) |
457 | continue; |
458 | |
459 | if (cycle != bridge->slaves[i].cycle) |
460 | continue; |
461 | |
462 | start = bridge->slaves[i].vme_base; |
463 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
464 | |
465 | if ((addr >= start) && ((addr + 1) < end)) { |
466 | offset = addr - bridge->slaves[i].vme_base; |
467 | loc = (u16 *)(bridge->slaves[i].buf_base + offset); |
468 | retval = *loc; |
469 | |
470 | break; |
471 | } |
472 | } |
473 | |
474 | fake_lm_check(bridge, addr, aspace, cycle); |
475 | |
476 | return retval; |
477 | } |
478 | |
479 | static noinline_for_stack u32 fake_vmeread32(struct fake_driver *bridge, |
480 | unsigned long long addr, |
481 | u32 aspace, u32 cycle) |
482 | { |
483 | u32 retval = 0xffffffff; |
484 | int i; |
485 | unsigned long long start, end, offset; |
486 | u32 *loc; |
487 | |
488 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
489 | if (aspace != bridge->slaves[i].aspace) |
490 | continue; |
491 | |
492 | if (cycle != bridge->slaves[i].cycle) |
493 | continue; |
494 | |
495 | start = bridge->slaves[i].vme_base; |
496 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
497 | |
498 | if ((addr >= start) && ((addr + 3) < end)) { |
499 | offset = addr - bridge->slaves[i].vme_base; |
500 | loc = (u32 *)(bridge->slaves[i].buf_base + offset); |
501 | retval = *loc; |
502 | |
503 | break; |
504 | } |
505 | } |
506 | |
507 | fake_lm_check(bridge, addr, aspace, cycle); |
508 | |
509 | return retval; |
510 | } |
511 | |
512 | static ssize_t fake_master_read(struct vme_master_resource *image, void *buf, |
513 | size_t count, loff_t offset) |
514 | { |
515 | int retval; |
516 | u32 aspace, cycle, dwidth; |
517 | struct vme_bridge *fake_bridge; |
518 | struct fake_driver *priv; |
519 | int i; |
520 | unsigned long long addr; |
521 | unsigned int done = 0; |
522 | unsigned int count32; |
523 | |
524 | fake_bridge = image->parent; |
525 | |
526 | priv = fake_bridge->driver_priv; |
527 | |
528 | i = image->number; |
529 | |
530 | addr = (unsigned long long)priv->masters[i].vme_base + offset; |
531 | aspace = priv->masters[i].aspace; |
532 | cycle = priv->masters[i].cycle; |
533 | dwidth = priv->masters[i].dwidth; |
534 | |
535 | spin_lock(lock: &image->lock); |
536 | |
537 | /* The following code handles VME address alignment. We cannot use |
538 | * memcpy_xxx here because it may cut data transfers in to 8-bit |
539 | * cycles when D16 or D32 cycles are required on the VME bus. |
540 | * On the other hand, the bridge itself assures that the maximum data |
541 | * cycle configured for the transfer is used and splits it |
542 | * automatically for non-aligned addresses, so we don't want the |
543 | * overhead of needlessly forcing small transfers for the entire cycle. |
544 | */ |
545 | if (addr & 0x1) { |
546 | *(u8 *)buf = fake_vmeread8(bridge: priv, addr, aspace, cycle); |
547 | done += 1; |
548 | if (done == count) |
549 | goto out; |
550 | } |
551 | if ((dwidth == VME_D16) || (dwidth == VME_D32)) { |
552 | if ((addr + done) & 0x2) { |
553 | if ((count - done) < 2) { |
554 | *(u8 *)(buf + done) = fake_vmeread8(bridge: priv, |
555 | addr: addr + done, aspace, cycle); |
556 | done += 1; |
557 | goto out; |
558 | } else { |
559 | *(u16 *)(buf + done) = fake_vmeread16(bridge: priv, |
560 | addr: addr + done, aspace, cycle); |
561 | done += 2; |
562 | } |
563 | } |
564 | } |
565 | |
566 | if (dwidth == VME_D32) { |
567 | count32 = (count - done) & ~0x3; |
568 | while (done < count32) { |
569 | *(u32 *)(buf + done) = fake_vmeread32(bridge: priv, addr: addr + done, |
570 | aspace, cycle); |
571 | done += 4; |
572 | } |
573 | } else if (dwidth == VME_D16) { |
574 | count32 = (count - done) & ~0x3; |
575 | while (done < count32) { |
576 | *(u16 *)(buf + done) = fake_vmeread16(bridge: priv, addr: addr + done, |
577 | aspace, cycle); |
578 | done += 2; |
579 | } |
580 | } else if (dwidth == VME_D8) { |
581 | count32 = (count - done); |
582 | while (done < count32) { |
583 | *(u8 *)(buf + done) = fake_vmeread8(bridge: priv, addr: addr + done, |
584 | aspace, cycle); |
585 | done += 1; |
586 | } |
587 | } |
588 | |
589 | if ((dwidth == VME_D16) || (dwidth == VME_D32)) { |
590 | if ((count - done) & 0x2) { |
591 | *(u16 *)(buf + done) = fake_vmeread16(bridge: priv, addr: addr + done, |
592 | aspace, cycle); |
593 | done += 2; |
594 | } |
595 | } |
596 | if ((count - done) & 0x1) { |
597 | *(u8 *)(buf + done) = fake_vmeread8(bridge: priv, addr: addr + done, aspace, |
598 | cycle); |
599 | done += 1; |
600 | } |
601 | |
602 | out: |
603 | retval = count; |
604 | |
605 | spin_unlock(lock: &image->lock); |
606 | |
607 | return retval; |
608 | } |
609 | |
610 | static noinline_for_stack void fake_vmewrite8(struct fake_driver *bridge, |
611 | u8 *buf, unsigned long long addr, |
612 | u32 aspace, u32 cycle) |
613 | { |
614 | int i; |
615 | unsigned long long start, end, offset; |
616 | u8 *loc; |
617 | |
618 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
619 | if (aspace != bridge->slaves[i].aspace) |
620 | continue; |
621 | |
622 | if (cycle != bridge->slaves[i].cycle) |
623 | continue; |
624 | |
625 | start = bridge->slaves[i].vme_base; |
626 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
627 | |
628 | if ((addr >= start) && (addr < end)) { |
629 | offset = addr - bridge->slaves[i].vme_base; |
630 | loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset); |
631 | *loc = *buf; |
632 | |
633 | break; |
634 | } |
635 | } |
636 | |
637 | fake_lm_check(bridge, addr, aspace, cycle); |
638 | } |
639 | |
640 | static noinline_for_stack void fake_vmewrite16(struct fake_driver *bridge, |
641 | u16 *buf, unsigned long long addr, |
642 | u32 aspace, u32 cycle) |
643 | { |
644 | int i; |
645 | unsigned long long start, end, offset; |
646 | u16 *loc; |
647 | |
648 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
649 | if (aspace != bridge->slaves[i].aspace) |
650 | continue; |
651 | |
652 | if (cycle != bridge->slaves[i].cycle) |
653 | continue; |
654 | |
655 | start = bridge->slaves[i].vme_base; |
656 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
657 | |
658 | if ((addr >= start) && ((addr + 1) < end)) { |
659 | offset = addr - bridge->slaves[i].vme_base; |
660 | loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset); |
661 | *loc = *buf; |
662 | |
663 | break; |
664 | } |
665 | } |
666 | |
667 | fake_lm_check(bridge, addr, aspace, cycle); |
668 | } |
669 | |
670 | static noinline_for_stack void fake_vmewrite32(struct fake_driver *bridge, |
671 | u32 *buf, unsigned long long addr, |
672 | u32 aspace, u32 cycle) |
673 | { |
674 | int i; |
675 | unsigned long long start, end, offset; |
676 | u32 *loc; |
677 | |
678 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
679 | if (aspace != bridge->slaves[i].aspace) |
680 | continue; |
681 | |
682 | if (cycle != bridge->slaves[i].cycle) |
683 | continue; |
684 | |
685 | start = bridge->slaves[i].vme_base; |
686 | end = bridge->slaves[i].vme_base + bridge->slaves[i].size; |
687 | |
688 | if ((addr >= start) && ((addr + 3) < end)) { |
689 | offset = addr - bridge->slaves[i].vme_base; |
690 | loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset); |
691 | *loc = *buf; |
692 | |
693 | break; |
694 | } |
695 | } |
696 | |
697 | fake_lm_check(bridge, addr, aspace, cycle); |
698 | } |
699 | |
700 | static ssize_t fake_master_write(struct vme_master_resource *image, void *buf, |
701 | size_t count, loff_t offset) |
702 | { |
703 | int retval = 0; |
704 | u32 aspace, cycle, dwidth; |
705 | unsigned long long addr; |
706 | int i; |
707 | unsigned int done = 0; |
708 | unsigned int count32; |
709 | |
710 | struct vme_bridge *fake_bridge; |
711 | struct fake_driver *bridge; |
712 | |
713 | fake_bridge = image->parent; |
714 | |
715 | bridge = fake_bridge->driver_priv; |
716 | |
717 | i = image->number; |
718 | |
719 | addr = bridge->masters[i].vme_base + offset; |
720 | aspace = bridge->masters[i].aspace; |
721 | cycle = bridge->masters[i].cycle; |
722 | dwidth = bridge->masters[i].dwidth; |
723 | |
724 | spin_lock(lock: &image->lock); |
725 | |
726 | /* Here we apply for the same strategy we do in master_read |
727 | * function in order to assure the correct cycles. |
728 | */ |
729 | if (addr & 0x1) { |
730 | fake_vmewrite8(bridge, buf: (u8 *)buf, addr, aspace, cycle); |
731 | done += 1; |
732 | if (done == count) |
733 | goto out; |
734 | } |
735 | |
736 | if ((dwidth == VME_D16) || (dwidth == VME_D32)) { |
737 | if ((addr + done) & 0x2) { |
738 | if ((count - done) < 2) { |
739 | fake_vmewrite8(bridge, buf: (u8 *)(buf + done), |
740 | addr: addr + done, aspace, cycle); |
741 | done += 1; |
742 | goto out; |
743 | } else { |
744 | fake_vmewrite16(bridge, buf: (u16 *)(buf + done), |
745 | addr: addr + done, aspace, cycle); |
746 | done += 2; |
747 | } |
748 | } |
749 | } |
750 | |
751 | if (dwidth == VME_D32) { |
752 | count32 = (count - done) & ~0x3; |
753 | while (done < count32) { |
754 | fake_vmewrite32(bridge, buf: (u32 *)(buf + done), |
755 | addr: addr + done, aspace, cycle); |
756 | done += 4; |
757 | } |
758 | } else if (dwidth == VME_D16) { |
759 | count32 = (count - done) & ~0x3; |
760 | while (done < count32) { |
761 | fake_vmewrite16(bridge, buf: (u16 *)(buf + done), |
762 | addr: addr + done, aspace, cycle); |
763 | done += 2; |
764 | } |
765 | } else if (dwidth == VME_D8) { |
766 | count32 = (count - done); |
767 | while (done < count32) { |
768 | fake_vmewrite8(bridge, buf: (u8 *)(buf + done), addr: addr + done, |
769 | aspace, cycle); |
770 | done += 1; |
771 | } |
772 | } |
773 | |
774 | if ((dwidth == VME_D16) || (dwidth == VME_D32)) { |
775 | if ((count - done) & 0x2) { |
776 | fake_vmewrite16(bridge, buf: (u16 *)(buf + done), |
777 | addr: addr + done, aspace, cycle); |
778 | done += 2; |
779 | } |
780 | } |
781 | |
782 | if ((count - done) & 0x1) { |
783 | fake_vmewrite8(bridge, buf: (u8 *)(buf + done), addr: addr + done, aspace, |
784 | cycle); |
785 | done += 1; |
786 | } |
787 | |
788 | out: |
789 | retval = count; |
790 | |
791 | spin_unlock(lock: &image->lock); |
792 | |
793 | return retval; |
794 | } |
795 | |
796 | /* |
797 | * Perform an RMW cycle on the VME bus. |
798 | * |
799 | * Requires a previously configured master window, returns final value. |
800 | */ |
801 | static unsigned int fake_master_rmw(struct vme_master_resource *image, |
802 | unsigned int mask, unsigned int compare, |
803 | unsigned int swap, loff_t offset) |
804 | { |
805 | u32 tmp, base; |
806 | u32 aspace, cycle; |
807 | int i; |
808 | struct fake_driver *bridge; |
809 | |
810 | bridge = image->parent->driver_priv; |
811 | |
812 | /* Find the PCI address that maps to the desired VME address */ |
813 | i = image->number; |
814 | |
815 | base = bridge->masters[i].vme_base; |
816 | aspace = bridge->masters[i].aspace; |
817 | cycle = bridge->masters[i].cycle; |
818 | |
819 | /* Lock image */ |
820 | spin_lock(lock: &image->lock); |
821 | |
822 | /* Read existing value */ |
823 | tmp = fake_vmeread32(bridge, addr: base + offset, aspace, cycle); |
824 | |
825 | /* Perform check */ |
826 | if ((tmp && mask) == (compare && mask)) { |
827 | tmp = tmp | (mask | swap); |
828 | tmp = tmp & (~mask | swap); |
829 | |
830 | /* Write back */ |
831 | fake_vmewrite32(bridge, buf: &tmp, addr: base + offset, aspace, cycle); |
832 | } |
833 | |
834 | /* Unlock image */ |
835 | spin_unlock(lock: &image->lock); |
836 | |
837 | return tmp; |
838 | } |
839 | |
840 | /* |
841 | * All 4 location monitors reside at the same base - this is therefore a |
842 | * system wide configuration. |
843 | * |
844 | * This does not enable the LM monitor - that should be done when the first |
845 | * callback is attached and disabled when the last callback is removed. |
846 | */ |
847 | static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base, |
848 | u32 aspace, u32 cycle) |
849 | { |
850 | int i; |
851 | struct vme_bridge *fake_bridge; |
852 | struct fake_driver *bridge; |
853 | |
854 | fake_bridge = lm->parent; |
855 | |
856 | bridge = fake_bridge->driver_priv; |
857 | |
858 | mutex_lock(&lm->mtx); |
859 | |
860 | /* If we already have a callback attached, we can't move it! */ |
861 | for (i = 0; i < lm->monitors; i++) { |
862 | if (bridge->lm_callback[i]) { |
863 | mutex_unlock(lock: &lm->mtx); |
864 | pr_err("Location monitor callback attached, can't reset\n" ); |
865 | return -EBUSY; |
866 | } |
867 | } |
868 | |
869 | switch (aspace) { |
870 | case VME_A16: |
871 | case VME_A24: |
872 | case VME_A32: |
873 | case VME_A64: |
874 | break; |
875 | default: |
876 | mutex_unlock(lock: &lm->mtx); |
877 | pr_err("Invalid address space\n" ); |
878 | return -EINVAL; |
879 | } |
880 | |
881 | bridge->lm_base = lm_base; |
882 | bridge->lm_aspace = aspace; |
883 | bridge->lm_cycle = cycle; |
884 | |
885 | mutex_unlock(lock: &lm->mtx); |
886 | |
887 | return 0; |
888 | } |
889 | |
890 | /* Get configuration of the callback monitor and return whether it is enabled |
891 | * or disabled. |
892 | */ |
893 | static int fake_lm_get(struct vme_lm_resource *lm, |
894 | unsigned long long *lm_base, |
895 | u32 *aspace, u32 *cycle) |
896 | { |
897 | struct fake_driver *bridge; |
898 | |
899 | bridge = lm->parent->driver_priv; |
900 | |
901 | mutex_lock(&lm->mtx); |
902 | |
903 | *lm_base = bridge->lm_base; |
904 | *aspace = bridge->lm_aspace; |
905 | *cycle = bridge->lm_cycle; |
906 | |
907 | mutex_unlock(lock: &lm->mtx); |
908 | |
909 | return bridge->lm_enabled; |
910 | } |
911 | |
912 | /* |
913 | * Attach a callback to a specific location monitor. |
914 | * |
915 | * Callback will be passed the monitor triggered. |
916 | */ |
917 | static int fake_lm_attach(struct vme_lm_resource *lm, int monitor, |
918 | void (*callback)(void *), void *data) |
919 | { |
920 | struct vme_bridge *fake_bridge; |
921 | struct fake_driver *bridge; |
922 | |
923 | fake_bridge = lm->parent; |
924 | |
925 | bridge = fake_bridge->driver_priv; |
926 | |
927 | mutex_lock(&lm->mtx); |
928 | |
929 | /* Ensure that the location monitor is configured - need PGM or DATA */ |
930 | if (bridge->lm_cycle == 0) { |
931 | mutex_unlock(lock: &lm->mtx); |
932 | pr_err("Location monitor not properly configured\n" ); |
933 | return -EINVAL; |
934 | } |
935 | |
936 | /* Check that a callback isn't already attached */ |
937 | if (bridge->lm_callback[monitor]) { |
938 | mutex_unlock(lock: &lm->mtx); |
939 | pr_err("Existing callback attached\n" ); |
940 | return -EBUSY; |
941 | } |
942 | |
943 | /* Attach callback */ |
944 | bridge->lm_callback[monitor] = callback; |
945 | bridge->lm_data[monitor] = data; |
946 | |
947 | /* Ensure that global Location Monitor Enable set */ |
948 | bridge->lm_enabled = 1; |
949 | |
950 | mutex_unlock(lock: &lm->mtx); |
951 | |
952 | return 0; |
953 | } |
954 | |
955 | /* |
956 | * Detach a callback function forn a specific location monitor. |
957 | */ |
958 | static int fake_lm_detach(struct vme_lm_resource *lm, int monitor) |
959 | { |
960 | u32 tmp; |
961 | int i; |
962 | struct fake_driver *bridge; |
963 | |
964 | bridge = lm->parent->driver_priv; |
965 | |
966 | mutex_lock(&lm->mtx); |
967 | |
968 | /* Detach callback */ |
969 | bridge->lm_callback[monitor] = NULL; |
970 | bridge->lm_data[monitor] = NULL; |
971 | |
972 | /* If all location monitors disabled, disable global Location Monitor */ |
973 | tmp = 0; |
974 | for (i = 0; i < lm->monitors; i++) { |
975 | if (bridge->lm_callback[i]) |
976 | tmp = 1; |
977 | } |
978 | |
979 | if (tmp == 0) |
980 | bridge->lm_enabled = 0; |
981 | |
982 | mutex_unlock(lock: &lm->mtx); |
983 | |
984 | return 0; |
985 | } |
986 | |
987 | /* |
988 | * Determine Geographical Addressing |
989 | */ |
990 | static int fake_slot_get(struct vme_bridge *fake_bridge) |
991 | { |
992 | return geoid; |
993 | } |
994 | |
995 | static void *fake_alloc_consistent(struct device *parent, size_t size, |
996 | dma_addr_t *dma) |
997 | { |
998 | void *alloc = kmalloc(size, GFP_KERNEL); |
999 | |
1000 | if (alloc) |
1001 | *dma = fake_ptr_to_pci(addr: alloc); |
1002 | |
1003 | return alloc; |
1004 | } |
1005 | |
1006 | static void fake_free_consistent(struct device *parent, size_t size, |
1007 | void *vaddr, dma_addr_t dma) |
1008 | { |
1009 | kfree(objp: vaddr); |
1010 | } |
1011 | |
1012 | /* |
1013 | * Configure CR/CSR space |
1014 | * |
1015 | * Access to the CR/CSR can be configured at power-up. The location of the |
1016 | * CR/CSR registers in the CR/CSR address space is determined by the boards |
1017 | * Geographic address. |
1018 | * |
1019 | * Each board has a 512kB window, with the highest 4kB being used for the |
1020 | * boards registers, this means there is a fix length 508kB window which must |
1021 | * be mapped onto PCI memory. |
1022 | */ |
1023 | static int fake_crcsr_init(struct vme_bridge *fake_bridge) |
1024 | { |
1025 | u32 vstat; |
1026 | struct fake_driver *bridge; |
1027 | |
1028 | bridge = fake_bridge->driver_priv; |
1029 | |
1030 | /* Allocate mem for CR/CSR image */ |
1031 | bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL); |
1032 | bridge->crcsr_bus = fake_ptr_to_pci(addr: bridge->crcsr_kernel); |
1033 | if (!bridge->crcsr_kernel) |
1034 | return -ENOMEM; |
1035 | |
1036 | vstat = fake_slot_get(fake_bridge); |
1037 | |
1038 | pr_info("CR/CSR Offset: %d\n" , vstat); |
1039 | |
1040 | return 0; |
1041 | } |
1042 | |
1043 | static void fake_crcsr_exit(struct vme_bridge *fake_bridge) |
1044 | { |
1045 | struct fake_driver *bridge; |
1046 | |
1047 | bridge = fake_bridge->driver_priv; |
1048 | |
1049 | kfree(objp: bridge->crcsr_kernel); |
1050 | } |
1051 | |
1052 | static int __init fake_init(void) |
1053 | { |
1054 | int retval, i; |
1055 | struct list_head *pos = NULL, *n; |
1056 | struct vme_bridge *fake_bridge; |
1057 | struct fake_driver *fake_device; |
1058 | struct vme_master_resource *master_image; |
1059 | struct vme_slave_resource *slave_image; |
1060 | struct vme_lm_resource *lm; |
1061 | |
1062 | /* We need a fake parent device */ |
1063 | vme_root = root_device_register("vme" ); |
1064 | if (IS_ERR(ptr: vme_root)) |
1065 | return PTR_ERR(ptr: vme_root); |
1066 | |
1067 | /* If we want to support more than one bridge at some point, we need to |
1068 | * dynamically allocate this so we get one per device. |
1069 | */ |
1070 | fake_bridge = kzalloc(size: sizeof(*fake_bridge), GFP_KERNEL); |
1071 | if (!fake_bridge) { |
1072 | retval = -ENOMEM; |
1073 | goto err_struct; |
1074 | } |
1075 | |
1076 | fake_device = kzalloc(size: sizeof(*fake_device), GFP_KERNEL); |
1077 | if (!fake_device) { |
1078 | retval = -ENOMEM; |
1079 | goto err_driver; |
1080 | } |
1081 | |
1082 | fake_bridge->driver_priv = fake_device; |
1083 | |
1084 | fake_bridge->parent = vme_root; |
1085 | |
1086 | fake_device->parent = fake_bridge; |
1087 | |
1088 | /* Initialize wait queues & mutual exclusion flags */ |
1089 | mutex_init(&fake_device->vme_int); |
1090 | mutex_init(&fake_bridge->irq_mtx); |
1091 | tasklet_init(t: &fake_device->int_tasklet, func: fake_VIRQ_tasklet, |
1092 | data: (unsigned long)fake_bridge); |
1093 | |
1094 | strscpy(fake_bridge->name, driver_name, sizeof(fake_bridge->name)); |
1095 | |
1096 | /* Add master windows to list */ |
1097 | INIT_LIST_HEAD(list: &fake_bridge->master_resources); |
1098 | for (i = 0; i < FAKE_MAX_MASTER; i++) { |
1099 | master_image = kmalloc(size: sizeof(*master_image), GFP_KERNEL); |
1100 | if (!master_image) { |
1101 | retval = -ENOMEM; |
1102 | goto err_master; |
1103 | } |
1104 | master_image->parent = fake_bridge; |
1105 | spin_lock_init(&master_image->lock); |
1106 | master_image->locked = 0; |
1107 | master_image->number = i; |
1108 | master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | |
1109 | VME_A64; |
1110 | master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | |
1111 | VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | |
1112 | VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | |
1113 | VME_PROG | VME_DATA; |
1114 | master_image->width_attr = VME_D16 | VME_D32; |
1115 | memset(&master_image->bus_resource, 0, |
1116 | sizeof(struct resource)); |
1117 | master_image->kern_base = NULL; |
1118 | list_add_tail(new: &master_image->list, |
1119 | head: &fake_bridge->master_resources); |
1120 | } |
1121 | |
1122 | /* Add slave windows to list */ |
1123 | INIT_LIST_HEAD(list: &fake_bridge->slave_resources); |
1124 | for (i = 0; i < FAKE_MAX_SLAVE; i++) { |
1125 | slave_image = kmalloc(size: sizeof(*slave_image), GFP_KERNEL); |
1126 | if (!slave_image) { |
1127 | retval = -ENOMEM; |
1128 | goto err_slave; |
1129 | } |
1130 | slave_image->parent = fake_bridge; |
1131 | mutex_init(&slave_image->mtx); |
1132 | slave_image->locked = 0; |
1133 | slave_image->number = i; |
1134 | slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 | |
1135 | VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 | |
1136 | VME_USER3 | VME_USER4; |
1137 | slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | |
1138 | VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 | |
1139 | VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER | |
1140 | VME_PROG | VME_DATA; |
1141 | list_add_tail(new: &slave_image->list, |
1142 | head: &fake_bridge->slave_resources); |
1143 | } |
1144 | |
1145 | /* Add location monitor to list */ |
1146 | INIT_LIST_HEAD(list: &fake_bridge->lm_resources); |
1147 | lm = kmalloc(size: sizeof(*lm), GFP_KERNEL); |
1148 | if (!lm) { |
1149 | retval = -ENOMEM; |
1150 | goto err_lm; |
1151 | } |
1152 | lm->parent = fake_bridge; |
1153 | mutex_init(&lm->mtx); |
1154 | lm->locked = 0; |
1155 | lm->number = 1; |
1156 | lm->monitors = 4; |
1157 | list_add_tail(new: &lm->list, head: &fake_bridge->lm_resources); |
1158 | |
1159 | fake_bridge->slave_get = fake_slave_get; |
1160 | fake_bridge->slave_set = fake_slave_set; |
1161 | fake_bridge->master_get = fake_master_get; |
1162 | fake_bridge->master_set = fake_master_set; |
1163 | fake_bridge->master_read = fake_master_read; |
1164 | fake_bridge->master_write = fake_master_write; |
1165 | fake_bridge->master_rmw = fake_master_rmw; |
1166 | fake_bridge->irq_set = fake_irq_set; |
1167 | fake_bridge->irq_generate = fake_irq_generate; |
1168 | fake_bridge->lm_set = fake_lm_set; |
1169 | fake_bridge->lm_get = fake_lm_get; |
1170 | fake_bridge->lm_attach = fake_lm_attach; |
1171 | fake_bridge->lm_detach = fake_lm_detach; |
1172 | fake_bridge->slot_get = fake_slot_get; |
1173 | fake_bridge->alloc_consistent = fake_alloc_consistent; |
1174 | fake_bridge->free_consistent = fake_free_consistent; |
1175 | |
1176 | pr_info("Board is%s the VME system controller\n" , |
1177 | (geoid == 1) ? "" : " not" ); |
1178 | |
1179 | pr_info("VME geographical address is set to %d\n" , geoid); |
1180 | |
1181 | retval = fake_crcsr_init(fake_bridge); |
1182 | if (retval) { |
1183 | pr_err("CR/CSR configuration failed.\n" ); |
1184 | goto err_crcsr; |
1185 | } |
1186 | |
1187 | retval = vme_register_bridge(fake_bridge); |
1188 | if (retval != 0) { |
1189 | pr_err("Chip Registration failed.\n" ); |
1190 | goto err_reg; |
1191 | } |
1192 | |
1193 | exit_pointer = fake_bridge; |
1194 | |
1195 | return 0; |
1196 | |
1197 | err_reg: |
1198 | fake_crcsr_exit(fake_bridge); |
1199 | err_crcsr: |
1200 | err_lm: |
1201 | /* resources are stored in link list */ |
1202 | list_for_each_safe(pos, n, &fake_bridge->lm_resources) { |
1203 | lm = list_entry(pos, struct vme_lm_resource, list); |
1204 | list_del(entry: pos); |
1205 | kfree(objp: lm); |
1206 | } |
1207 | err_slave: |
1208 | /* resources are stored in link list */ |
1209 | list_for_each_safe(pos, n, &fake_bridge->slave_resources) { |
1210 | slave_image = list_entry(pos, struct vme_slave_resource, list); |
1211 | list_del(entry: pos); |
1212 | kfree(objp: slave_image); |
1213 | } |
1214 | err_master: |
1215 | /* resources are stored in link list */ |
1216 | list_for_each_safe(pos, n, &fake_bridge->master_resources) { |
1217 | master_image = list_entry(pos, struct vme_master_resource, |
1218 | list); |
1219 | list_del(entry: pos); |
1220 | kfree(objp: master_image); |
1221 | } |
1222 | |
1223 | kfree(objp: fake_device); |
1224 | err_driver: |
1225 | kfree(objp: fake_bridge); |
1226 | err_struct: |
1227 | return retval; |
1228 | } |
1229 | |
1230 | static void __exit fake_exit(void) |
1231 | { |
1232 | struct list_head *pos = NULL; |
1233 | struct list_head *tmplist; |
1234 | struct vme_master_resource *master_image; |
1235 | struct vme_slave_resource *slave_image; |
1236 | int i; |
1237 | struct vme_bridge *fake_bridge; |
1238 | struct fake_driver *bridge; |
1239 | |
1240 | fake_bridge = exit_pointer; |
1241 | |
1242 | bridge = fake_bridge->driver_priv; |
1243 | |
1244 | pr_debug("Driver is being unloaded.\n" ); |
1245 | |
1246 | /* |
1247 | * Shutdown all inbound and outbound windows. |
1248 | */ |
1249 | for (i = 0; i < FAKE_MAX_MASTER; i++) |
1250 | bridge->masters[i].enabled = 0; |
1251 | |
1252 | for (i = 0; i < FAKE_MAX_SLAVE; i++) |
1253 | bridge->slaves[i].enabled = 0; |
1254 | |
1255 | /* |
1256 | * Shutdown Location monitor. |
1257 | */ |
1258 | bridge->lm_enabled = 0; |
1259 | |
1260 | vme_unregister_bridge(fake_bridge); |
1261 | |
1262 | fake_crcsr_exit(fake_bridge); |
1263 | /* resources are stored in link list */ |
1264 | list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) { |
1265 | slave_image = list_entry(pos, struct vme_slave_resource, list); |
1266 | list_del(entry: pos); |
1267 | kfree(objp: slave_image); |
1268 | } |
1269 | |
1270 | /* resources are stored in link list */ |
1271 | list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) { |
1272 | master_image = list_entry(pos, struct vme_master_resource, |
1273 | list); |
1274 | list_del(entry: pos); |
1275 | kfree(objp: master_image); |
1276 | } |
1277 | |
1278 | kfree(objp: fake_bridge->driver_priv); |
1279 | |
1280 | kfree(objp: fake_bridge); |
1281 | |
1282 | root_device_unregister(root: vme_root); |
1283 | } |
1284 | |
1285 | MODULE_PARM_DESC(geoid, "Set geographical addressing" ); |
1286 | module_param(geoid, int, 0); |
1287 | |
1288 | MODULE_DESCRIPTION("Fake VME bridge driver" ); |
1289 | MODULE_LICENSE("GPL" ); |
1290 | |
1291 | module_init(fake_init); |
1292 | module_exit(fake_exit); |
1293 | |