1 #include <linux/module.h>
2 #include <linux/interrupt.h>
5 #include "modulbus_register.h"
8 #define DRIVER_NAME "vmodio"
9 #define PFX DRIVER_NAME ": "
12 * this module is invoked as
13 * $ insmod vmodio lun=0,1,4
14 * base_address=0x1200,0xA800,0x6000
17 static int lun
[MAX_DEVICES
];
18 static unsigned int nlun
;
19 module_param_array(lun
, int, &nlun
, S_IRUGO
);
21 static unsigned long base_address
[MAX_DEVICES
];
22 static unsigned int nbase_address
;
23 module_param_array(base_address
, ulong
, &nbase_address
, S_IRUGO
);
25 static int irq
[MAX_DEVICES
];
26 static unsigned int nirq
;
27 module_param_array(irq
, int, &nirq
, S_IRUGO
);
29 static int irq_to_lun
[MAX_DEVICES
];
30 static int lun_slot_to_irq
[MAX_DEVICES
][VMODIO_SLOTS
];
32 /* description of a vmodio module */
34 int lun
; /* logical unit number */
35 unsigned long vme_addr
; /* vme base address */
36 unsigned long vaddr
; /* virtual address of MODULBUS
41 static struct vmodio device_table
[MAX_DEVICES
];
44 /* Matrix for register the isr callback functions */
50 static struct mz_callback
51 mezzanines_callback
[MAX_DEVICES
][VMODIO_SLOTS
];
53 /* obtain the irq of slot from a base_irq in slot 0 */
54 static inline int vmodio_irq_shift(int base_irq
, int slot
)
56 return base_irq
- (0xe - (~(1<<slot
) & 0xf));
59 static inline int upper_nibble_of(int byte
)
61 return 0xf & (byte
>> 4);
64 static int interrupt_to_slot
[] = {
71 /* get the slot an irq corresponds to */
72 static inline int irq_to_slot(int tmp
)
74 return interrupt_to_slot
[tmp
&0xf];
77 /* get the device struct corresponding to a lun */
78 static struct vmodio
*lun_to_dev(int lun
)
82 for (i
= 0; i
< devices
; i
++) {
83 struct vmodio
*dev
= &device_table
[i
];
90 /* map vmodio VME address space */
91 static struct pdparam_master param
= {
92 .iack
= 1, /* no iack */
93 .rdpref
= 0, /* no VME read prefetch option */
94 .wrpost
= 0, /* no VME write posting option */
95 .swap
= 1, /* VME auto swap option */
96 .dum
= { VME_PG_SHARED
, /* window is sharable */
98 0, }, /* window is sharable */
101 static unsigned long vmodio_map(unsigned long base_address
)
103 return find_controller(base_address
, VMODIO_WINDOW_LENGTH
,
104 VMODIO_ADDRESS_MODIFIER
, 0, VMODIO_DATA_SIZE
, ¶m
);
107 /* slot memory-mapped I/O offsets */
108 static int vmodio_offsets
[VMODIO_SLOTS
] = {
116 * @brief Get virtual address of a mezzanine board AS
118 * VMOD/IO assigns a memory-mapped IO area to each slot of slot0..3
119 * by the rule slot_address = base_address + slot#*0x200.
121 * @param board_number - logical module number of VMOD/IO card
122 * @param board_positio - slot the requesting mz is plugged in
123 * @param address_space_number
124 * - must be 1 (only one address space available)
125 * @return 0 on success
126 * @return != 0 on failure
128 static int get_address_space(
129 struct carrier_as
*asp
,
130 int board_number
, int board_position
, int address_space_number
)
134 /* VMOD/IO has a single space */
135 if (address_space_number
!= 1) {
136 printk(KERN_ERR PFX
"invalid address space request\n");
139 dev
= lun_to_dev(board_number
);
141 printk(KERN_ERR PFX
"non-existent lun %d\n", board_number
);
144 if ((board_position
< 0) || (board_position
>= VMODIO_SLOTS
)) {
145 printk(KERN_ERR PFX
"invalid VMOD/IO board position %d\n", board_position
);
149 /* parameters ok, set up mapping information */
150 asp
->address
= dev
->vaddr
+ vmodio_offsets
[board_position
];
153 asp
->is_big_endian
= 1;
158 * @brief Wrapper around get_address_space
160 * Same parameters and semantics, only intended for export (via
161 * linux EXPORT_SYMBOL or some LynxOS kludge)
163 static int vmodio_get_address_space(
164 struct carrier_as
*as
,
167 int address_space_number
)
169 return get_address_space(as
, board_number
, board_position
, address_space_number
);
171 EXPORT_SYMBOL_GPL(vmodio_get_address_space
);
173 static inline int within_bounds(int board
, int position
)
175 return board
>= 0 && board
< devices
&&
176 position
>= 0 && position
< VMODIO_SLOTS
;
180 * @brief Save a isr_callback of each mezzanine connected.
182 * VMOD/IO saves the isr callback function of each mezzanine's driver
183 * to call it when an interrupt occurs.
185 * @param isr_callback - IRQ callback function to be called.
186 * @param board_number - lun of the carrier where the requesting mezzanine is plugged in
187 * @param board_position - slot the requesting mezzanine is plugged in
188 * @return 0 on success
189 * @retrun != 0 on failure
191 static int register_isr(isrcb_t callback
,
193 int board_number
, int board_position
)
195 struct mz_callback
*entry
;
197 /* Adds the isr_callback if the carrier number and slot are correct. */
198 if (!within_bounds(board_number
, board_position
)) {
200 "Invalid VMOD/IO board number %d or board position %d\n",
201 board_number
, board_position
);
205 entry
= &mezzanines_callback
[board_number
][board_position
];
206 entry
->callback
= callback
;
211 static int vmodio_interrupt(void *irq_id
)
213 int carrier_number
= -1;
214 short board_position
= -1;
215 int irqno
= *(int *)irq_id
;
217 struct mz_callback
*entry
;
219 /* Get the interrupt vector to know the lun of the matched carrier */
220 carrier_number
= irq_to_lun
[upper_nibble_of(irqno
)];
222 /* Get the interrupt vector to know the slot */
223 board_position
= irq_to_slot(irqno
);
225 if (!within_bounds(carrier_number
, board_position
)) {
227 "invalid board_number interrupt:"
228 "carrier %d board_position %d\n",
229 carrier_number
, board_position
);
233 entry
= &mezzanines_callback
[carrier_number
][board_position
];
234 callback
= entry
->callback
;
235 if (entry
->callback
== NULL
|| callback(entry
->dev
, NULL
) < 0)
241 static int device_init(struct vmodio
*dev
, int lun
, unsigned long base_address
, int base_irq
)
247 dev
->vme_addr
= base_address
;
248 dev
->vaddr
= vmodio_map(base_address
);
250 if (dev
->vaddr
== -1)
253 /* Upper nibble of irq corresponds to a single VMODIO module */
254 irq_to_lun
[upper_nibble_of(base_irq
)] = lun
;
257 * The irq corresponding to the first slot is passed as argument
258 * to the driver To the rest of slots, the irq is calculated by
259 * substracting constants 0, 1, 3, 7.
261 for (i
= 0; i
< VMODIO_SLOTS
; i
++) {
262 int slot_irq
= vmodio_irq_shift(base_irq
, i
);
263 dev
->irq
[i
] = slot_irq
;
264 lun_slot_to_irq
[lun
][i
] = slot_irq
;
265 ret
= vme_request_irq(slot_irq
, vmodio_interrupt
, &dev
->irq
[i
], "vmodio");
268 "Cannot register an irq "
269 "to the device %d, error %d\n",
273 printk(KERN_INFO PFX
"registered IRQ %d for %s device,"
274 "lun = %d, slot %d\n", slot_irq
, DRIVER_NAME
, lun
, i
);
279 static int __init
init(void)
284 printk(KERN_INFO PFX
"initializing driver\n");
286 /* Initialize the needed matrix for IRQ */
287 for (i
= 0; i
< MAX_DEVICES
; i
++){
289 lun_slot_to_irq
[i
][0] = -1;
290 lun_slot_to_irq
[i
][1] = -1;
291 lun_slot_to_irq
[i
][2] = -1;
292 lun_slot_to_irq
[i
][3] = -1;
295 if (nlun
>= MAX_DEVICES
) {
296 printk(KERN_ERR PFX
"too many devices (%d)\n", nlun
);
299 if (nlun
!= nbase_address
|| nlun
!= nirq
) {
301 "Given %d luns but %d addresses and %d irqs\n",
302 nlun
, nbase_address
, nirq
);
306 if (modulbus_carrier_register(DRIVER_NAME
, get_address_space
, register_isr
) != 0) {
307 printk(KERN_ERR PFX
"could not register %s module\n",
311 printk(KERN_INFO PFX
"registered as %s carrier\n", DRIVER_NAME
);
314 for (device
= 0; device
< nlun
; device
++) {
315 struct vmodio
*dev
= &device_table
[device
];
317 if (device_init(dev
, lun
[device
], base_address
[device
], irq
[device
])) {
318 printk(KERN_ERR PFX
"map failed! not configuring lun %d\n",
320 goto failed_register
;
322 printk(KERN_INFO PFX
"mapped at virtual address 0x%08lx\n",
326 printk(KERN_INFO PFX
"%d devices configured\n", devices
);
331 modulbus_carrier_unregister(DRIVER_NAME
);
333 printk(KERN_ERR PFX
"failed to init, exit\n");
337 static void __exit
exit(void)
341 printk(KERN_INFO PFX
"uninstalling driver\n");
342 for(i
= 0; i
< MAX_DEVICES
; i
++)
343 for(j
= 0; j
< VMODIO_SLOTS
; j
++)
344 if(lun_slot_to_irq
[i
][j
] >= 0)
345 vme_free_irq(lun_slot_to_irq
[i
][j
]);
346 modulbus_carrier_unregister(DRIVER_NAME
);
352 MODULE_AUTHOR("Juan David Gonzalez Cobas");
353 MODULE_LICENSE("GPL");
354 MODULE_DESCRIPTION("VMODIO driver");