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