Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This software may be used and distributed according to the terms | |
3 | * of the GNU General Public License, incorporated herein by reference. | |
4 | * | |
5 | */ | |
6 | ||
7 | #include <linux/module.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/delay.h> | |
11 | #include "includes.h" | |
12 | #include "hardware.h" | |
13 | #include "card.h" | |
14 | ||
15 | MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card"); | |
16 | MODULE_AUTHOR("Spellcaster Telecommunications Inc."); | |
17 | MODULE_LICENSE("GPL"); | |
18 | ||
19 | board *sc_adapter[MAX_CARDS]; | |
20 | int cinst; | |
21 | ||
22 | static char devname[] = "scX"; | |
e3ca5e76 | 23 | static const char version[] = "2.0b1"; |
1da177e4 | 24 | |
e3ca5e76 | 25 | static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" }; |
1da177e4 LT |
26 | |
27 | /* insmod set parameters */ | |
28 | static unsigned int io[] = {0,0,0,0}; | |
29 | static unsigned char irq[] = {0,0,0,0}; | |
30 | static unsigned long ram[] = {0,0,0,0}; | |
31 | static int do_reset = 0; | |
32 | ||
33 | module_param_array(io, int, NULL, 0); | |
34 | module_param_array(irq, int, NULL, 0); | |
35 | module_param_array(ram, int, NULL, 0); | |
36 | module_param(do_reset, bool, 0); | |
37 | ||
7d12e780 | 38 | extern irqreturn_t interrupt_handler(int, void *); |
1da177e4 LT |
39 | extern int sndpkt(int, int, int, struct sk_buff *); |
40 | extern int command(isdn_ctrl *); | |
41 | extern int indicate_status(int, int, ulong, char*); | |
42 | extern int reset(int); | |
43 | ||
e3ca5e76 | 44 | static int identify_board(unsigned long, unsigned int); |
1da177e4 LT |
45 | |
46 | static int __init sc_init(void) | |
47 | { | |
48 | int b = -1; | |
49 | int i, j; | |
50 | int status = -ENODEV; | |
51 | ||
52 | unsigned long memsize = 0; | |
53 | unsigned long features = 0; | |
54 | isdn_if *interface; | |
55 | unsigned char channels; | |
56 | unsigned char pgport; | |
57 | unsigned long magic; | |
58 | int model; | |
59 | int last_base = IOBASE_MIN; | |
60 | int probe_exhasted = 0; | |
61 | ||
62 | #ifdef MODULE | |
63 | pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version); | |
64 | #else | |
65 | pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version); | |
66 | #endif | |
67 | pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n"); | |
68 | ||
69 | while(b++ < MAX_CARDS - 1) { | |
70 | pr_debug("Probing for adapter #%d\n", b); | |
71 | /* | |
72 | * Initialize reusable variables | |
73 | */ | |
74 | model = -1; | |
75 | magic = 0; | |
76 | channels = 0; | |
77 | pgport = 0; | |
78 | ||
79 | /* | |
80 | * See if we should probe for IO base | |
81 | */ | |
82 | pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b], | |
83 | io[b] == 0 ? "will" : "won't"); | |
84 | if(io[b]) { | |
85 | /* | |
86 | * No, I/O Base has been provided | |
87 | */ | |
88 | for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) { | |
89 | if(!request_region(io[b] + i * 0x400, 1, "sc test")) { | |
fb911ee8 | 90 | pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400); |
1da177e4 LT |
91 | io[b] = 0; |
92 | break; | |
93 | } else | |
94 | release_region(io[b] + i * 0x400, 1); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Confirm the I/O Address with a test | |
99 | */ | |
100 | if(io[b] == 0) { | |
76fd0209 | 101 | pr_debug("I/O Address invalid.\n"); |
1da177e4 LT |
102 | continue; |
103 | } | |
104 | ||
105 | outb(0x18, io[b] + 0x400 * EXP_PAGE0); | |
106 | if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { | |
76fd0209 JG |
107 | pr_debug("I/O Base 0x%x fails test\n", |
108 | io[b] + 0x400 * EXP_PAGE0); | |
1da177e4 LT |
109 | continue; |
110 | } | |
111 | } | |
112 | else { | |
113 | /* | |
114 | * Yes, probe for I/O Base | |
115 | */ | |
116 | if(probe_exhasted) { | |
117 | pr_debug("All probe addresses exhasted, skipping\n"); | |
118 | continue; | |
119 | } | |
120 | pr_debug("Probing for I/O...\n"); | |
121 | for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) { | |
122 | int found_io = 1; | |
123 | if (i == IOBASE_MAX) { | |
124 | probe_exhasted = 1; /* No more addresses to probe */ | |
125 | pr_debug("End of Probes\n"); | |
126 | } | |
127 | last_base = i + IOBASE_OFFSET; | |
128 | pr_debug(" checking 0x%x...", i); | |
129 | for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) { | |
130 | if(!request_region(i + j * 0x400, 1, "sc test")) { | |
131 | pr_debug("Failed\n"); | |
132 | found_io = 0; | |
133 | break; | |
134 | } else | |
135 | release_region(i + j * 0x400, 1); | |
136 | } | |
137 | ||
138 | if(found_io) { | |
139 | io[b] = i; | |
140 | outb(0x18, io[b] + 0x400 * EXP_PAGE0); | |
141 | if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { | |
142 | pr_debug("Failed by test\n"); | |
143 | continue; | |
144 | } | |
145 | pr_debug("Passed\n"); | |
146 | break; | |
147 | } | |
148 | } | |
149 | if(probe_exhasted) { | |
150 | continue; | |
151 | } | |
152 | } | |
153 | ||
154 | /* | |
155 | * See if we should probe for shared RAM | |
156 | */ | |
157 | if(do_reset) { | |
158 | pr_debug("Doing a SAFE probe reset\n"); | |
159 | outb(0xFF, io[b] + RESET_OFFSET); | |
160 | msleep_interruptible(10000); | |
161 | } | |
76fd0209 JG |
162 | pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b, |
163 | ram[b], ram[b] == 0 ? "will" : "won't"); | |
1da177e4 LT |
164 | |
165 | if(ram[b]) { | |
166 | /* | |
167 | * No, the RAM base has been provided | |
168 | * Just look for a signature and ID the | |
169 | * board model | |
170 | */ | |
171 | if(request_region(ram[b], SRAM_PAGESIZE, "sc test")) { | |
76fd0209 | 172 | pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]); |
1da177e4 LT |
173 | model = identify_board(ram[b], io[b]); |
174 | release_region(ram[b], SRAM_PAGESIZE); | |
175 | } | |
176 | } | |
177 | else { | |
178 | /* | |
179 | * Yes, probe for free RAM and look for | |
180 | * a signature and id the board model | |
181 | */ | |
182 | for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) { | |
183 | pr_debug("Checking RAM address 0x%x...\n", i); | |
184 | if(request_region(i, SRAM_PAGESIZE, "sc test")) { | |
fb911ee8 | 185 | pr_debug(" request_region succeeded\n"); |
1da177e4 LT |
186 | model = identify_board(i, io[b]); |
187 | release_region(i, SRAM_PAGESIZE); | |
188 | if (model >= 0) { | |
189 | pr_debug(" Identified a %s\n", | |
190 | boardname[model]); | |
191 | ram[b] = i; | |
192 | break; | |
193 | } | |
194 | pr_debug(" Unidentifed or inaccessible\n"); | |
195 | continue; | |
196 | } | |
197 | pr_debug(" request failed\n"); | |
198 | } | |
199 | } | |
200 | /* | |
201 | * See if we found free RAM and the board model | |
202 | */ | |
203 | if(!ram[b] || model < 0) { | |
204 | /* | |
205 | * Nope, there was no place in RAM for the | |
206 | * board, or it couldn't be identified | |
207 | */ | |
76fd0209 | 208 | pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]); |
1da177e4 LT |
209 | continue; |
210 | } | |
211 | ||
212 | /* | |
213 | * Set the board's magic number, memory size and page register | |
214 | */ | |
215 | switch(model) { | |
216 | case PRI_BOARD: | |
217 | channels = 23; | |
218 | magic = 0x20000; | |
219 | memsize = 0x100000; | |
220 | features = PRI_FEATURES; | |
221 | break; | |
222 | ||
223 | case BRI_BOARD: | |
224 | case POTS_BOARD: | |
225 | channels = 2; | |
226 | magic = 0x60000; | |
227 | memsize = 0x10000; | |
228 | features = BRI_FEATURES; | |
229 | break; | |
230 | } | |
231 | switch(ram[b] >> 12 & 0x0F) { | |
232 | case 0x0: | |
233 | pr_debug("RAM Page register set to EXP_PAGE0\n"); | |
234 | pgport = EXP_PAGE0; | |
235 | break; | |
236 | ||
237 | case 0x4: | |
238 | pr_debug("RAM Page register set to EXP_PAGE1\n"); | |
239 | pgport = EXP_PAGE1; | |
240 | break; | |
241 | ||
242 | case 0x8: | |
243 | pr_debug("RAM Page register set to EXP_PAGE2\n"); | |
244 | pgport = EXP_PAGE2; | |
245 | break; | |
246 | ||
247 | case 0xC: | |
248 | pr_debug("RAM Page register set to EXP_PAGE3\n"); | |
249 | pgport = EXP_PAGE3; | |
250 | break; | |
251 | ||
252 | default: | |
253 | pr_debug("RAM base address doesn't fall on 16K boundary\n"); | |
254 | continue; | |
255 | } | |
256 | ||
257 | pr_debug("current IRQ: %d b: %d\n",irq[b],b); | |
258 | ||
259 | /* | |
260 | * Make sure we got an IRQ | |
261 | */ | |
262 | if(!irq[b]) { | |
263 | /* | |
264 | * No interrupt could be used | |
265 | */ | |
266 | pr_debug("Failed to acquire an IRQ line\n"); | |
267 | continue; | |
268 | } | |
269 | ||
270 | /* | |
271 | * Horray! We found a board, Make sure we can register | |
272 | * it with ISDN4Linux | |
273 | */ | |
41f96935 | 274 | interface = kzalloc(sizeof(isdn_if), GFP_KERNEL); |
1da177e4 LT |
275 | if (interface == NULL) { |
276 | /* | |
277 | * Oops, can't malloc isdn_if | |
278 | */ | |
279 | continue; | |
280 | } | |
1da177e4 LT |
281 | |
282 | interface->owner = THIS_MODULE; | |
283 | interface->hl_hdrlen = 0; | |
284 | interface->channels = channels; | |
285 | interface->maxbufsize = BUFFER_SIZE; | |
286 | interface->features = features; | |
287 | interface->writebuf_skb = sndpkt; | |
288 | interface->writecmd = NULL; | |
289 | interface->command = command; | |
290 | strcpy(interface->id, devname); | |
291 | interface->id[2] = '0' + cinst; | |
292 | ||
293 | /* | |
294 | * Allocate the board structure | |
295 | */ | |
41f96935 | 296 | sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL); |
1da177e4 LT |
297 | if (sc_adapter[cinst] == NULL) { |
298 | /* | |
299 | * Oops, can't alloc memory for the board | |
300 | */ | |
301 | kfree(interface); | |
302 | continue; | |
303 | } | |
1da177e4 LT |
304 | spin_lock_init(&sc_adapter[cinst]->lock); |
305 | ||
306 | if(!register_isdn(interface)) { | |
307 | /* | |
308 | * Oops, couldn't register for some reason | |
309 | */ | |
310 | kfree(interface); | |
311 | kfree(sc_adapter[cinst]); | |
312 | continue; | |
313 | } | |
314 | ||
315 | sc_adapter[cinst]->card = interface; | |
316 | sc_adapter[cinst]->driverId = interface->channels; | |
317 | strcpy(sc_adapter[cinst]->devicename, interface->id); | |
318 | sc_adapter[cinst]->nChannels = channels; | |
319 | sc_adapter[cinst]->ramsize = memsize; | |
320 | sc_adapter[cinst]->shmem_magic = magic; | |
321 | sc_adapter[cinst]->shmem_pgport = pgport; | |
322 | sc_adapter[cinst]->StartOnReset = 1; | |
323 | ||
324 | /* | |
325 | * Allocate channels status structures | |
326 | */ | |
41f96935 | 327 | sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL); |
1da177e4 LT |
328 | if (sc_adapter[cinst]->channel == NULL) { |
329 | /* | |
330 | * Oops, can't alloc memory for the channels | |
331 | */ | |
332 | indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ | |
333 | kfree(interface); | |
334 | kfree(sc_adapter[cinst]); | |
335 | continue; | |
336 | } | |
1da177e4 LT |
337 | |
338 | /* | |
339 | * Lock down the hardware resources | |
340 | */ | |
341 | sc_adapter[cinst]->interrupt = irq[b]; | |
342 | if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler, | |
9ba02bec | 343 | IRQF_DISABLED, interface->id, NULL)) |
1da177e4 LT |
344 | { |
345 | kfree(sc_adapter[cinst]->channel); | |
346 | indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ | |
347 | kfree(interface); | |
348 | kfree(sc_adapter[cinst]); | |
349 | continue; | |
350 | ||
351 | } | |
352 | sc_adapter[cinst]->iobase = io[b]; | |
353 | for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) { | |
354 | sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400; | |
355 | request_region(sc_adapter[cinst]->ioport[i], 1, | |
356 | interface->id); | |
357 | pr_debug("Requesting I/O Port %#x\n", | |
358 | sc_adapter[cinst]->ioport[i]); | |
359 | } | |
360 | sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2; | |
361 | request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1, | |
362 | interface->id); | |
363 | pr_debug("Requesting I/O Port %#x\n", | |
364 | sc_adapter[cinst]->ioport[IRQ_SELECT]); | |
365 | sc_adapter[cinst]->rambase = ram[b]; | |
366 | request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE, | |
367 | interface->id); | |
368 | ||
369 | pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", | |
370 | sc_adapter[cinst]->devicename, | |
371 | sc_adapter[cinst]->driverId, | |
372 | boardname[model], channels, irq[b], io[b], ram[b]); | |
373 | ||
374 | /* | |
375 | * reset the adapter to put things in motion | |
376 | */ | |
377 | reset(cinst); | |
378 | ||
379 | cinst++; | |
380 | status = 0; | |
381 | } | |
382 | if (status) | |
383 | pr_info("Failed to find any adapters, driver unloaded\n"); | |
384 | return status; | |
385 | } | |
386 | ||
387 | static void __exit sc_exit(void) | |
388 | { | |
389 | int i, j; | |
390 | ||
391 | for(i = 0 ; i < cinst ; i++) { | |
392 | pr_debug("Cleaning up after adapter %d\n", i); | |
393 | /* | |
394 | * kill the timers | |
395 | */ | |
396 | del_timer(&(sc_adapter[i]->reset_timer)); | |
397 | del_timer(&(sc_adapter[i]->stat_timer)); | |
398 | ||
399 | /* | |
400 | * Tell I4L we're toast | |
401 | */ | |
402 | indicate_status(i, ISDN_STAT_STOP, 0, NULL); | |
403 | indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL); | |
404 | ||
405 | /* | |
406 | * Release shared RAM | |
407 | */ | |
408 | release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE); | |
409 | ||
410 | /* | |
411 | * Release the IRQ | |
412 | */ | |
413 | FREE_IRQ(sc_adapter[i]->interrupt, NULL); | |
414 | ||
415 | /* | |
416 | * Reset for a clean start | |
417 | */ | |
418 | outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]); | |
419 | ||
420 | /* | |
421 | * Release the I/O Port regions | |
422 | */ | |
423 | for(j = 0 ; j < MAX_IO_REGS - 1; j++) { | |
424 | release_region(sc_adapter[i]->ioport[j], 1); | |
425 | pr_debug("Releasing I/O Port %#x\n", | |
426 | sc_adapter[i]->ioport[j]); | |
427 | } | |
428 | release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1); | |
429 | pr_debug("Releasing I/O Port %#x\n", | |
430 | sc_adapter[i]->ioport[IRQ_SELECT]); | |
431 | ||
432 | /* | |
433 | * Release any memory we alloced | |
434 | */ | |
435 | kfree(sc_adapter[i]->channel); | |
436 | kfree(sc_adapter[i]->card); | |
437 | kfree(sc_adapter[i]); | |
438 | } | |
439 | pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n"); | |
440 | } | |
441 | ||
e3ca5e76 | 442 | static int identify_board(unsigned long rambase, unsigned int iobase) |
1da177e4 LT |
443 | { |
444 | unsigned int pgport; | |
445 | unsigned long sig; | |
446 | DualPortMemory *dpm; | |
447 | RspMessage rcvmsg; | |
448 | ReqMessage sndmsg; | |
449 | HWConfig_pl hwci; | |
450 | int x; | |
451 | ||
76fd0209 | 452 | pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n", |
1da177e4 LT |
453 | rambase, iobase); |
454 | ||
455 | /* | |
456 | * Enable the base pointer | |
457 | */ | |
458 | outb(rambase >> 12, iobase + 0x2c00); | |
459 | ||
460 | switch(rambase >> 12 & 0x0F) { | |
461 | case 0x0: | |
462 | pgport = iobase + PG0_OFFSET; | |
463 | pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET); | |
464 | break; | |
465 | ||
466 | case 0x4: | |
467 | pgport = iobase + PG1_OFFSET; | |
468 | pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET); | |
469 | break; | |
470 | ||
471 | case 0x8: | |
472 | pgport = iobase + PG2_OFFSET; | |
473 | pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET); | |
474 | break; | |
475 | ||
476 | case 0xC: | |
477 | pgport = iobase + PG3_OFFSET; | |
478 | pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET); | |
479 | break; | |
480 | default: | |
481 | pr_debug("Invalid rambase 0x%lx\n", rambase); | |
482 | return -1; | |
483 | } | |
484 | ||
485 | /* | |
486 | * Try to identify a PRI card | |
487 | */ | |
488 | outb(PRI_BASEPG_VAL, pgport); | |
489 | msleep_interruptible(1000); | |
490 | sig = readl(rambase + SIG_OFFSET); | |
76fd0209 | 491 | pr_debug("Looking for a signature, got 0x%lx\n", sig); |
1da177e4 LT |
492 | if(sig == SIGNATURE) |
493 | return PRI_BOARD; | |
494 | ||
495 | /* | |
496 | * Try to identify a PRI card | |
497 | */ | |
498 | outb(BRI_BASEPG_VAL, pgport); | |
499 | msleep_interruptible(1000); | |
500 | sig = readl(rambase + SIG_OFFSET); | |
76fd0209 | 501 | pr_debug("Looking for a signature, got 0x%lx\n", sig); |
1da177e4 LT |
502 | if(sig == SIGNATURE) |
503 | return BRI_BOARD; | |
504 | ||
505 | return -1; | |
506 | ||
507 | /* | |
508 | * Try to spot a card | |
509 | */ | |
510 | sig = readl(rambase + SIG_OFFSET); | |
76fd0209 | 511 | pr_debug("Looking for a signature, got 0x%lx\n", sig); |
1da177e4 LT |
512 | if(sig != SIGNATURE) |
513 | return -1; | |
514 | ||
515 | dpm = (DualPortMemory *) rambase; | |
516 | ||
517 | memset(&sndmsg, 0, MSG_LEN); | |
518 | sndmsg.msg_byte_cnt = 3; | |
519 | sndmsg.type = cmReqType1; | |
520 | sndmsg.class = cmReqClass0; | |
521 | sndmsg.code = cmReqHWConfig; | |
522 | memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN); | |
523 | outb(0, iobase + 0x400); | |
524 | pr_debug("Sent HWConfig message\n"); | |
525 | /* | |
526 | * Wait for the response | |
527 | */ | |
528 | x = 0; | |
529 | while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) { | |
24763c48 | 530 | schedule_timeout_interruptible(1); |
1da177e4 LT |
531 | x++; |
532 | } | |
533 | if(x == 100) { | |
534 | pr_debug("Timeout waiting for response\n"); | |
535 | return -1; | |
536 | } | |
537 | ||
538 | memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN); | |
539 | pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status); | |
540 | memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl)); | |
76fd0209 | 541 | pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n" |
1da177e4 LT |
542 | " Part: %s, Rev: %s\n", |
543 | hwci.st_u_sense ? "S/T" : "U", hwci.ram_size, | |
544 | hwci.serial_no, hwci.part_no, hwci.rev_no); | |
545 | ||
546 | if(!strncmp(PRI_PARTNO, hwci.part_no, 6)) | |
547 | return PRI_BOARD; | |
548 | if(!strncmp(BRI_PARTNO, hwci.part_no, 6)) | |
549 | return BRI_BOARD; | |
550 | ||
551 | return -1; | |
552 | } | |
553 | ||
554 | module_init(sc_init); | |
555 | module_exit(sc_exit); |