11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This software may be used and distributed according to the terms
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the GNU General Public License, incorporated herein by reference.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
11d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h>
125a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "includes.h"
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "hardware.h"
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "card.h"
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Spellcaster Telecommunications Inc.");
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsboard *sc_adapter[MAX_CARDS];
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cinst;
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char devname[] = "scX";
25e3ca5e762c2aca373f1762cbc622ebe20fd20869Adrian Bunkstatic const char version[] = "2.0b1";
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27e3ca5e762c2aca373f1762cbc622ebe20fd20869Adrian Bunkstatic const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* insmod set parameters */
30475be4d85a274d0961593db41cf85689db1d583cJoe Perchesstatic unsigned int io[] = {0, 0, 0, 0};
31475be4d85a274d0961593db41cf85689db1d583cJoe Perchesstatic unsigned char irq[] = {0, 0, 0, 0};
32475be4d85a274d0961593db41cf85689db1d583cJoe Perchesstatic unsigned long ram[] = {0, 0, 0, 0};
3390ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool do_reset = 0;
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
36b9e48de110ec64bdfd4b83358d0e286551bb110cGeert Uytterhoevenmodule_param_array(irq, byte, NULL, 0);
37b9e48de110ec64bdfd4b83358d0e286551bb110cGeert Uytterhoevenmodule_param_array(ram, long, NULL, 0);
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(do_reset, bool, 0);
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
40e3ca5e762c2aca373f1762cbc622ebe20fd20869Adrian Bunkstatic int identify_board(unsigned long, unsigned int);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init sc_init(void)
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int b = -1;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status = -ENODEV;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long memsize = 0;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long features = 0;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	isdn_if *interface;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char channels;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char pgport;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long magic;
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int model;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int last_base = IOBASE_MIN;
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int probe_exhasted = 0;
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef MODULE
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
65475be4d85a274d0961593db41cf85689db1d583cJoe Perches	while (b++ < MAX_CARDS - 1) {
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Probing for adapter #%d\n", b);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Initialize reusable variables
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		model = -1;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		magic = 0;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		channels = 0;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgport = 0;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
75475be4d85a274d0961593db41cf85689db1d583cJoe Perches		/*
76475be4d85a274d0961593db41cf85689db1d583cJoe Perches		 * See if we should probe for IO base
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
79475be4d85a274d0961593db41cf85689db1d583cJoe Perches			 io[b] == 0 ? "will" : "won't");
80475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (io[b]) {
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * No, I/O Base has been provided
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
84475be4d85a274d0961593db41cf85689db1d583cJoe Perches			for (i = 0; i < MAX_IO_REGS - 1; i++) {
85475be4d85a274d0961593db41cf85689db1d583cJoe Perches				if (!request_region(io[b] + i * 0x400, 1, "sc test")) {
86fb911ee849756fc6c609dddded92d9207ff3fb29Peter Osterlund					pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400);
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					io[b] = 0;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					release_region(io[b] + i * 0x400, 1);
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Confirm the I/O Address with a test
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
96475be4d85a274d0961593db41cf85689db1d583cJoe Perches			if (io[b] == 0) {
9776fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik				pr_debug("I/O Address invalid.\n");
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(0x18, io[b] + 0x400 * EXP_PAGE0);
102475be4d85a274d0961593db41cf85689db1d583cJoe Perches			if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
10376fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik				pr_debug("I/O Base 0x%x fails test\n",
10476fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik					 io[b] + 0x400 * EXP_PAGE0);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else {
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Yes, probe for I/O Base
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
112475be4d85a274d0961593db41cf85689db1d583cJoe Perches			if (probe_exhasted) {
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pr_debug("All probe addresses exhasted, skipping\n");
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("Probing for I/O...\n");
117475be4d85a274d0961593db41cf85689db1d583cJoe Perches			for (i = last_base; i <= IOBASE_MAX; i += IOBASE_OFFSET) {
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int found_io = 1;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (i == IOBASE_MAX) {
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					probe_exhasted = 1; /* No more addresses to probe */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pr_debug("End of Probes\n");
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				last_base = i + IOBASE_OFFSET;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pr_debug("  checking 0x%x...", i);
125475be4d85a274d0961593db41cf85689db1d583cJoe Perches				for (j = 0; j < MAX_IO_REGS - 1; j++) {
126475be4d85a274d0961593db41cf85689db1d583cJoe Perches					if (!request_region(i + j * 0x400, 1, "sc test")) {
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						pr_debug("Failed\n");
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						found_io = 0;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					} else
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						release_region(i + j * 0x400, 1);
132475be4d85a274d0961593db41cf85689db1d583cJoe Perches				}
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
134475be4d85a274d0961593db41cf85689db1d583cJoe Perches				if (found_io) {
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					io[b] = i;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					outb(0x18, io[b] + 0x400 * EXP_PAGE0);
137475be4d85a274d0961593db41cf85689db1d583cJoe Perches					if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						pr_debug("Failed by test\n");
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						continue;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pr_debug("Passed\n");
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
145475be4d85a274d0961593db41cf85689db1d583cJoe Perches			if (probe_exhasted) {
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * See if we should probe for shared RAM
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
153475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (do_reset) {
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("Doing a SAFE probe reset\n");
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(0xFF, io[b] + RESET_OFFSET);
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msleep_interruptible(10000);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
15876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik		pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b,
159475be4d85a274d0961593db41cf85689db1d583cJoe Perches			 ram[b], ram[b] == 0 ? "will" : "won't");
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (ram[b]) {
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * No, the RAM base has been provided
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Just look for a signature and ID the
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * board model
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
167475be4d85a274d0961593db41cf85689db1d583cJoe Perches			if (request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
16876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik				pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]);
169475be4d85a274d0961593db41cf85689db1d583cJoe Perches				model = identify_board(ram[b], io[b]);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				release_region(ram[b], SRAM_PAGESIZE);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else {
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Yes, probe for free RAM and look for
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * a signature and id the board model
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
178475be4d85a274d0961593db41cf85689db1d583cJoe Perches			for (i = SRAM_MIN; i < SRAM_MAX; i += SRAM_PAGESIZE) {
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pr_debug("Checking RAM address 0x%x...\n", i);
180475be4d85a274d0961593db41cf85689db1d583cJoe Perches				if (request_region(i, SRAM_PAGESIZE, "sc test")) {
181fb911ee849756fc6c609dddded92d9207ff3fb29Peter Osterlund					pr_debug("  request_region succeeded\n");
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					model = identify_board(i, io[b]);
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					release_region(i, SRAM_PAGESIZE);
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (model >= 0) {
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						pr_debug("  Identified a %s\n",
186475be4d85a274d0961593db41cf85689db1d583cJoe Perches							 boardname[model]);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ram[b] = i;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pr_debug("  Unidentifed or inaccessible\n");
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					continue;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pr_debug("  request failed\n");
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * See if we found free RAM and the board model
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
199475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (!ram[b] || model < 0) {
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Nope, there was no place in RAM for the
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * board, or it couldn't be identified
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
204475be4d85a274d0961593db41cf85689db1d583cJoe Perches			pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]);
205475be4d85a274d0961593db41cf85689db1d583cJoe Perches			continue;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Set the board's magic number, memory size and page register
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
211475be4d85a274d0961593db41cf85689db1d583cJoe Perches		switch (model) {
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case PRI_BOARD:
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			channels = 23;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			magic = 0x20000;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memsize = 0x100000;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			features = PRI_FEATURES;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case BRI_BOARD:
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case POTS_BOARD:
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			channels = 2;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			magic = 0x60000;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memsize = 0x10000;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			features = BRI_FEATURES;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
227475be4d85a274d0961593db41cf85689db1d583cJoe Perches		switch (ram[b] >> 12 & 0x0F) {
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x0:
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("RAM Page register set to EXP_PAGE0\n");
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgport = EXP_PAGE0;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x4:
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("RAM Page register set to EXP_PAGE1\n");
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgport = EXP_PAGE1;
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x8:
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("RAM Page register set to EXP_PAGE2\n");
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgport = EXP_PAGE2;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0xC:
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("RAM Page register set to EXP_PAGE3\n");
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pgport = EXP_PAGE3;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("RAM base address doesn't fall on 16K boundary\n");
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
253475be4d85a274d0961593db41cf85689db1d583cJoe Perches		pr_debug("current IRQ: %d  b: %d\n", irq[b], b);
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Make sure we got an IRQ
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
258475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (!irq[b]) {
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * No interrupt could be used
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("Failed to acquire an IRQ line\n");
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Horray! We found a board, Make sure we can register
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * it with ISDN4Linux
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
27041f96935b4c41daea2c4dbbf137960375cf764c1Burman Yan		interface = kzalloc(sizeof(isdn_if), GFP_KERNEL);
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (interface == NULL) {
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Oops, can't malloc isdn_if
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->owner = THIS_MODULE;
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->hl_hdrlen = 0;
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->channels = channels;
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->maxbufsize = BUFFER_SIZE;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->features = features;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->writebuf_skb = sndpkt;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->writecmd = NULL;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->command = command;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		strcpy(interface->id, devname);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		interface->id[2] = '0' + cinst;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Allocate the board structure
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
29241f96935b4c41daea2c4dbbf137960375cf764c1Burman Yan		sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL);
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sc_adapter[cinst] == NULL) {
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Oops, can't alloc memory for the board
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(interface);
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_init(&sc_adapter[cinst]->lock);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
302475be4d85a274d0961593db41cf85689db1d583cJoe Perches		if (!register_isdn(interface)) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Oops, couldn't register for some reason
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(interface);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(sc_adapter[cinst]);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->card = interface;
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->driverId = interface->channels;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		strcpy(sc_adapter[cinst]->devicename, interface->id);
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->nChannels = channels;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->ramsize = memsize;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->shmem_magic = magic;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->shmem_pgport = pgport;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->StartOnReset = 1;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Allocate channels status structures
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
32341f96935b4c41daea2c4dbbf137960375cf764c1Burman Yan		sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sc_adapter[cinst]->channel == NULL) {
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/*
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 * Oops, can't alloc memory for the channels
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 */
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(interface);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(sc_adapter[cinst]);
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Lock down the hardware resources
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->interrupt = irq[b];
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
33933235ca458b725bab5367f1e50562965c76af5caMichael Opdenacker				0, interface->id,
340080eb42f31a8a6dde1568f906692d9914cdfbfe8Jeff Garzik				(void *)(unsigned long) cinst))
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(sc_adapter[cinst]->channel);
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL);	/* Fix me */
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(interface);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			kfree(sc_adapter[cinst]);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
347475be4d85a274d0961593db41cf85689db1d583cJoe Perches
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->iobase = io[b];
350475be4d85a274d0961593db41cf85689db1d583cJoe Perches		for (i = 0; i < MAX_IO_REGS - 1; i++) {
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			request_region(sc_adapter[cinst]->ioport[i], 1,
353475be4d85a274d0961593db41cf85689db1d583cJoe Perches				       interface->id);
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("Requesting I/O Port %#x\n",
355475be4d85a274d0961593db41cf85689db1d583cJoe Perches				 sc_adapter[cinst]->ioport[i]);
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
359475be4d85a274d0961593db41cf85689db1d583cJoe Perches			       interface->id);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Requesting I/O Port %#x\n",
361475be4d85a274d0961593db41cf85689db1d583cJoe Perches			 sc_adapter[cinst]->ioport[IRQ_SELECT]);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sc_adapter[cinst]->rambase = ram[b];
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
364475be4d85a274d0961593db41cf85689db1d583cJoe Perches			       interface->id);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
366475be4d85a274d0961593db41cf85689db1d583cJoe Perches		pr_info("  %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n",
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sc_adapter[cinst]->devicename,
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sc_adapter[cinst]->driverId,
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			boardname[model], channels, irq[b], io[b], ram[b]);
370475be4d85a274d0961593db41cf85689db1d583cJoe Perches
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * reset the adapter to put things in motion
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		reset(cinst);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cinst++;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = 0;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
379475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (status)
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_info("Failed to find any adapters, driver unloaded\n");
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit sc_exit(void)
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, j;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
388475be4d85a274d0961593db41cf85689db1d583cJoe Perches	for (i = 0; i < cinst; i++) {
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Cleaning up after adapter %d\n", i);
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * kill the timers
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3930c295e44dea607be5f4cc2d19ce98afbd77e2619Julia Lawall		del_timer_sync(&(sc_adapter[i]->reset_timer));
3940c295e44dea607be5f4cc2d19ce98afbd77e2619Julia Lawall		del_timer_sync(&(sc_adapter[i]->stat_timer));
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Tell I4L we're toast
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		indicate_status(i, ISDN_STAT_STOP, 0, NULL);
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Release shared RAM
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Release the IRQ
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
410345225c8e4a4adad9eb261db26aebcd3b87055adFernando Luis Vázquez Cao		free_irq(sc_adapter[i]->interrupt, NULL);
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Reset for a clean start
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Release the I/O Port regions
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
420475be4d85a274d0961593db41cf85689db1d583cJoe Perches		for (j = 0; j < MAX_IO_REGS - 1; j++) {
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			release_region(sc_adapter[i]->ioport[j], 1);
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			pr_debug("Releasing I/O Port %#x\n",
423475be4d85a274d0961593db41cf85689db1d583cJoe Perches				 sc_adapter[i]->ioport[j]);
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Releasing I/O Port %#x\n",
427475be4d85a274d0961593db41cf85689db1d583cJoe Perches			 sc_adapter[i]->ioport[IRQ_SELECT]);
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Release any memory we alloced
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(sc_adapter[i]->channel);
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(sc_adapter[i]->card);
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(sc_adapter[i]);
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
439e3ca5e762c2aca373f1762cbc622ebe20fd20869Adrian Bunkstatic int identify_board(unsigned long rambase, unsigned int iobase)
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int pgport;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long sig;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DualPortMemory *dpm;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	RspMessage rcvmsg;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ReqMessage sndmsg;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HWConfig_pl hwci;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int x;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
44976fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik	pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n",
450475be4d85a274d0961593db41cf85689db1d583cJoe Perches		 rambase, iobase);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Enable the base pointer
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(rambase >> 12, iobase + 0x2c00);
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
457475be4d85a274d0961593db41cf85689db1d583cJoe Perches	switch (rambase >> 12 & 0x0F) {
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0x0:
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgport = iobase + PG0_OFFSET;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
462475be4d85a274d0961593db41cf85689db1d583cJoe Perches
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0x4:
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgport = iobase + PG1_OFFSET;
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0x8:
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgport = iobase + PG2_OFFSET;
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0xC:
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pgport = iobase + PG3_OFFSET;
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Invalid rambase 0x%lx\n", rambase);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Try to identify a PRI card
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(PRI_BASEPG_VAL, pgport);
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	msleep_interruptible(1000);
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sig = readl(rambase + SIG_OFFSET);
48876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik	pr_debug("Looking for a signature, got 0x%lx\n", sig);
489475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (sig == SIGNATURE)
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return PRI_BOARD;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Try to identify a PRI card
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(BRI_BASEPG_VAL, pgport);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	msleep_interruptible(1000);
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sig = readl(rambase + SIG_OFFSET);
49876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik	pr_debug("Looking for a signature, got 0x%lx\n", sig);
499475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (sig == SIGNATURE)
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return BRI_BOARD;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Try to spot a card
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sig = readl(rambase + SIG_OFFSET);
50876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik	pr_debug("Looking for a signature, got 0x%lx\n", sig);
509475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (sig != SIGNATURE)
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dpm = (DualPortMemory *) rambase;
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(&sndmsg, 0, MSG_LEN);
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sndmsg.msg_byte_cnt = 3;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sndmsg.type = cmReqType1;
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sndmsg.class = cmReqClass0;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sndmsg.code = cmReqHWConfig;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0, iobase + 0x400);
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_debug("Sent HWConfig message\n");
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Wait for the response
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x = 0;
526475be4d85a274d0961593db41cf85689db1d583cJoe Perches	while ((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
52724763c48a3c9cdf0a138038b51a7fca65859cd78Nishanth Aravamudan		schedule_timeout_interruptible(1);
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		x++;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
530475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (x == 100) {
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pr_debug("Timeout waiting for response\n");
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -1;
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
53876fd020937f2d09f76a4cd8dbae1f3bec640ff0bJeff Garzik	pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n"
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 "                 Part: %s, Rev: %s\n",
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 hwci.serial_no, hwci.part_no, hwci.rev_no);
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
543475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (!strncmp(PRI_PARTNO, hwci.part_no, 6))
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return PRI_BOARD;
545475be4d85a274d0961593db41cf85689db1d583cJoe Perches	if (!strncmp(BRI_PARTNO, hwci.part_no, 6))
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return BRI_BOARD;
547475be4d85a274d0961593db41cf85689db1d583cJoe Perches
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(sc_init);
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(sc_exit);
553