vmod/vmodttl: fixed bug related to luns not ordered and/or not starting from zero.
[ht-drivers.git] / vmod / driver / vmodio.c
blobbc1e7c6bbc0e5b7345cf7d92807ce067caf89971
1 #include <linux/module.h>
2 #include <linux/interrupt.h>
3 #include <vmebus.h>
4 #include "vmodio.h"
5 #include "modulbus_register.h"
7 #define MAX_DEVICES 16
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
15 * irq=126,130,142
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 */
33 struct vmodio {
34 int lun; /* logical unit number */
35 unsigned long vme_addr; /* vme base address */
36 unsigned long vaddr; /* virtual address of MODULBUS
37 space */
38 int irq[4]; /* IRQ */
41 static struct vmodio device_table[MAX_DEVICES];
42 static int devices;
44 /* Matrix for register the isr callback functions */
45 struct mz_callback {
46 isrcb_t callback;
47 void *dev;
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[] = {
65 -1, -1, -1, -1,
66 -1, -1, -1, 3,
67 -1, -1, -1, 2,
68 -1, 1, 0, -1,
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)
80 int i = 0;
82 for (i = 0; i < devices; i++) {
83 struct vmodio *dev = &device_table[i];
84 if (dev->lun == lun)
85 return dev;
87 return NULL;
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 */
97 0, /* XPC ADP-type */
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, &param);
107 /* slot memory-mapped I/O offsets */
108 static int vmodio_offsets[VMODIO_SLOTS] = {
109 VMODIO_SLOT0,
110 VMODIO_SLOT1,
111 VMODIO_SLOT2,
112 VMODIO_SLOT3,
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)
132 struct vmodio *dev;
134 /* VMOD/IO has a single space */
135 if (address_space_number != 1) {
136 printk(KERN_ERR PFX "invalid address space request\n");
137 return -1;
139 dev = lun_to_dev(board_number);
140 if (dev == NULL) {
141 printk(KERN_ERR PFX "non-existent lun %d\n", board_number);
142 return -1;
144 if ((board_position < 0) || (board_position >= VMODIO_SLOTS)) {
145 printk(KERN_ERR PFX "invalid VMOD/IO board position %d\n", board_position);
146 return -1;
149 /* parameters ok, set up mapping information */
150 asp->address = dev->vaddr + vmodio_offsets[board_position];
151 asp->size = 0x200;
152 asp->width = 16;
153 asp->is_big_endian = 1;
154 return 0;
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,
165 int board_number,
166 int board_position,
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,
192 void *dev,
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)) {
199 printk(KERN_ERR PFX
200 "Invalid VMOD/IO board number %d or board position %d\n",
201 board_number, board_position);
202 return -1;
205 entry = &mezzanines_callback[board_number][board_position];
206 entry->callback = callback;
207 entry->dev = dev;
208 return 0;
211 static int vmodio_interrupt(void *irq_id)
213 int carrier_number = -1;
214 short board_position = -1;
215 int irqno = *(int *)irq_id;
216 isrcb_t callback;
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)) {
226 printk(KERN_ERR PFX
227 "invalid board_number interrupt:"
228 "carrier %d board_position %d\n",
229 carrier_number, board_position);
230 return IRQ_NONE;
233 entry = &mezzanines_callback[carrier_number][board_position];
234 callback = entry->callback;
235 if (entry->callback == NULL || callback(entry->dev, NULL) < 0)
236 return IRQ_NONE;
238 return IRQ_HANDLED;
241 static int device_init(struct vmodio *dev, int lun, unsigned long base_address, int base_irq)
243 int ret;
244 int i;
246 dev->lun = lun;
247 dev->vme_addr = base_address;
248 dev->vaddr = vmodio_map(base_address);
250 if (dev->vaddr == -1)
251 return -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");
266 if (ret < 0) {
267 printk(KERN_ERR PFX
268 "Cannot register an irq "
269 "to the device %d, error %d\n",
270 dev->lun, ret);
271 return -1;
273 printk(KERN_INFO PFX "registered IRQ %d for %s device,"
274 "lun = %d, slot %d\n", slot_irq, DRIVER_NAME, lun, i);
276 return 0;
279 static int __init init(void)
281 int device = 0;
282 int i;
284 printk(KERN_INFO PFX "initializing driver\n");
286 /* Initialize the needed matrix for IRQ */
287 for (i = 0; i < MAX_DEVICES; i++){
288 irq_to_lun[i] = -1;
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);
297 goto failed_init;
299 if (nlun != nbase_address || nlun != nirq) {
300 printk(KERN_ERR PFX
301 "Given %d luns but %d addresses and %d irqs\n",
302 nlun, nbase_address, nirq);
303 goto failed_init;
306 if (modulbus_carrier_register(DRIVER_NAME, get_address_space, register_isr) != 0) {
307 printk(KERN_ERR PFX "could not register %s module\n",
308 DRIVER_NAME);
309 goto failed_init;
311 printk(KERN_INFO PFX "registered as %s carrier\n", DRIVER_NAME);
313 devices = 0;
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",
319 dev->lun);
320 goto failed_register;
322 printk(KERN_INFO PFX "mapped at virtual address 0x%08lx\n",
323 dev->vaddr);
324 devices++;
326 printk(KERN_INFO PFX "%d devices configured\n", devices);
328 return 0;
330 failed_register:
331 modulbus_carrier_unregister(DRIVER_NAME);
332 failed_init:
333 printk(KERN_ERR PFX "failed to init, exit\n");
334 return -1;
337 static void __exit exit(void)
339 int i, j;
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);
349 module_init(init);
350 module_exit(exit);
352 MODULE_AUTHOR("Juan David Gonzalez Cobas");
353 MODULE_LICENSE("GPL");
354 MODULE_DESCRIPTION("VMODIO driver");