diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/Documentation/Configure.help linux-2.0.35-mca/Documentation/Configure.help --- linux/Documentation/Configure.help Sat Aug 8 21:35:40 1998 +++ linux-2.0.35-mca/Documentation/Configure.help Sat Aug 8 22:20:45 1998 @@ -311,6 +311,14 @@ devices are not supported yet. See the Documentation/ide.txt and promise.c files for more info. +PS/2 ESDI harddisk support +CONFIG_BLK_DEV_PS2 + This is the type of drive used in many of the IBM PS/2 machines, as + well as some Thinkpad 700 and 720 laptops. CONFIG_MCA must be enabled + for this to work. In some cases, you may be required to provide the + drive geometry through the eda= and edb= kernel boot parameter. + Thinkpad users will need the tp720 kernel boot flag. + XT harddisk support CONFIG_BLK_DEV_XD Very old 8 bit hard disk controllers used in the IBM XT computer. @@ -822,6 +830,13 @@ boot time ("man dmesg"), please follow the instructions at the top of include/linux/pci.h. +MCA bus support +CONFIG_MCA + Micro Channel (MCA) is the bus used on the IBM PS/2 line and a small + number of machines from other vendors. Say Y if you have a MCA bus. + For more information on MCA, see Documentation/mca.txt or the MCA + Linux web page at "http://glycerine.itsmm.uni.edu/mca/" + PCI bridge optimization (experimental) CONFIG_PCI_OPTIMIZE This can improve access times for some hardware devices under @@ -2068,6 +2083,24 @@ from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. +IBMMCA SCSI support +CONFIG_SCSI_IBMMCA + This is support for the IBM SCSI adapter found in many of the PS/2 + series. CONFIG_MCA must be set for this to work. If the adapter + isn't found during boot (a common problem for models 56, 57, 76, and + 77) you'll need to use the ibmmcascsi=io_port, where io_port of the + SCSI subsystem is usually 0x3540, but if that doesn't work check your + reference diskette. SCSI device scanning order can be specified by + ibmmcascsi=normal for Industry Standard ordering (lowest id maps to + /dev/sda), otherwise IBM/ANSI order will be used. + +FutureDomain MCS 600/700 (and IBM OEM) SCSI support +CONFIG_SCSI_FD_MCS + This driver supports Future Domain MCS-600/700 MCA SCSI adapters and + IBM Fast SCSI Adapter/A (MCS-700 OEM) Adapters. CONFIG_MCA must be + set for this to work. Multiple cards are auto detected and supported. + This driver also supports the SCSI part of the Reply SB/SCSI Adapter. + IOMEGA Parallel Port ZIP drive SCSI support CONFIG_SCSI_PPA This driver supports the parallel port version of IOMEGA's ZIP drive @@ -2558,6 +2591,11 @@ Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. +SMC Ultra MCA support +CONFIG_ULTRA_MCA + This is the Micro Channel variant of the SMC Ultra (actually, it says + for the Elite series). It probably won't work as a module. + SMC 9194 Support CONFIG_SMC9194 This is support for the SMC9xxx based Ethernet cards. Choose this @@ -2648,7 +2686,19 @@ Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. -3c509/3c579 support +3c523 support +CONFIG_ELMC + This is a Micro Channel ethernet adapter. You need to set CONFIG_MCA + to use this driver. It's also available as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. If you plan to use more than + one network card under linux, read the Multiple-Ethernet-mini-HOWTO, + available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Note that + this driver hasn't been tested with multiple ethernet cards. + +3c509/3c529(MCA)/3c579 support CONFIG_EL3 If you have a network (ethernet) card belonging to the 3Com EtherLinkIII series, say Y and read the Ethernet-HOWTO, available @@ -2831,7 +2881,7 @@ Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. -EtherExpress support +EtherExpress/EtherExpressMC support CONFIG_EEXPRESS If you have an EtherExpress16 network (ethernet) card, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/Documentation/mca.txt linux-2.0.35-mca/Documentation/mca.txt --- linux/Documentation/mca.txt Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/Documentation/mca.txt Sat Aug 8 22:20:45 1998 @@ -0,0 +1,320 @@ +i386 Micro Channel Architecture Support +======================================= + +MCA support is enabled using the CONFIG_MCA define. A machine with a MCA +bus will have the kernel variable MCA_bus set, assuming the BIOS feature +bits are set properly (see arch/i386/boot/setup.S for information on +how this detection is done). + +Adapter Detection +================= + +The ideal MCA adapter detection is done through the use of the +Programmable Option Select registers. Generic functions for doing +this have been added in include/linux/mca.h and arch/i386/kernel/mca.c. +Everything needed to detect adapters and read (and write) configuration +information is there. A number of MCA-specific drivers already use +this. The typical probe code looks like the following: + + #include + + unsigned char pos2, pos3, pos4, pos5; + struct device* dev; + int slot; + + if( MCA_bus ) { + slot = mca_find_adapter( ADAPTER_ID, 0 ); + if( slot == MCA_NOTFOUND ) { + return ENODEV; + } + /* optional - see below */ + mca_set_adapter_name( slot, "adapter name & description" ); + mca_set_adapter_procfn( slot, dev_getinfo, dev ); + + /* read the POS registers. Most devices only use 2 and 3 */ + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + pos4 = mca_read_stored_pos( slot, 4 ); + pos5 = mca_read_stored_pos( slot, 5 ); + } else { + return ENODEV; + } + + /* extract configuration from pos[2345] and set everything up */ + +Loadable modules should modify this to test that the specified IRQ and +IO ports (plus whatever other stuff) match. See 3c523.c for example +code (actually, smc-mca.c has a slightly more complex example that can +handle a list of adapter ids). + +Keep in mind that devices should never directly access the POS registers +(via inb(), outb(), etc). While it's generally safe, there is a small +potential for blowing up hardware when it's done at the wrong time. +Furthermore, accessing a POS register disables a device temporarily. +This is usually okay during startup, but do _you_ want to rely on it? +During initial configuration, mca_init() reads all the POS registers +into memory. mca_read_stored_pos() accesses that data. mca_read_pos() +and mca_write_pos() are also available for (safer) direct POS access, +but their use is _highly_ discouraged. mca_write_pos() is particularly +dangerous, as it is possible for adapters to be put in inconsistent +states (i.e. sharing IO address, etc) and may result in crashes, toasted +hardware, and blindness. + +User level drivers (such as the AGX X server) can use /proc/mca/pos to +find adapters (see below). + +Some MCA adapters can also be detected via the usual ISA-style device +probing (many SCSI adapters, for example). This sort of thing is highly +discouraged. Perfectly good information is available telling you what's +there, so there's no excuse for messing with random IO ports. However, +we MCA people still appreciate any ISA-style driver that will work with +our hardware. You take what you can get... + +Level-Triggered Interrupts +========================== + +Because MCA uses level-triggered interrupts, a few problems arise with +what might best be described as the ISA mindset and its effects on +drivers. These sorts of problems are expected to become less common as +more people use shared IRQs on PCI machines. + +In general, an interrupt must be acknowledged not only at the ICU (which +is done automagically by the kernel), but at the device level. In +particular, IRQ 0 must be reset after a timer interrupt (now done in +arch/i386/kernel/time.c) or the first timer interrupt hangs the system. +There were also problems with the 1.3.x floppy drivers, but that seems +to have been fixed. + +IRQs are also shareable, and most MCA-specific devices should be coded +with shared IRQs in mind. + +/proc/mca +========= + +/proc/mca is a directory containing various files for adapters and +other stuff. + + /proc/mca/pos Straight listing of POS registers + /proc/mca/slot[1-8] Information on adapter in specific slot + /proc/mca/video Same for integrated video + /proc/mca/scsi Same for integrated SCSI + /proc/mca/machine Machine information + +See Appendix A for a sample. + +Device drivers can easily add their own information function for +specific slots (including integrated ones) via the +mca_set_adapter_procfn() call. Drivers that support this are ESDI, IBM +SCSI, and 3c523. If a device is also a module, make sure that the proc +function is removed in the module cleanup. This will require storing +the slot information in a private structure somewhere. See the 3c523 +driver for details. + +Your typical proc function will look something like this: + + static int + dev_getinfo( char* buf, int slot, void* d ) { + struct device* dev = (struct device*) d; + int len = 0; + + len += sprintf( buf+len, "Device: %s\n", dev->name ); + len += sprintf( buf+len, "IRQ: %d\n", dev->irq ); + len += sprintf( buf+len, "IO Port: %#lx-%#lx\n", ... ); + ... + + return len; + } + +Some of the standard MCA information will already be printed, so don't +bother repeating it. Don't try putting in more than 3K of information. + +Enable this function with: + mca_set_adapter_procfn( slot, dev_getinfo, dev ); + +Disable it with: + mca_set_adapter_procfn( slot, NULL, NULL ); + +It is also recommended that, even if you don't write a proc function, to +set the name of the adapter (i.e. "PS/2 ESDI Controller") via +mca_set_adapter_name( int slot, char* name ). + +MCA Device Drivers +================== + +Currently, there are a number of MCA-specific device drivers. + +1) PS/2 ESDI + drivers/block/ps2esdi.c + include/linux/ps2esdi.h + Uses major number 36, and should use /dev files /dev/eda, /dev/edb. + Supports two drives, but only one controller. May use the + command-line args "ed=cyl,head,sec" and "tp720". + +2) PS/2 SCSI + drivers/scsi/ibmmca.c + drivers/scsi/ibmmca.h + The driver for the IBM SCSI subsystem. Includes both integrated + controllers and adapter cards. May require command-line arg + "ibmmcascsi=io_port" to force detection of an adapter. If you have a + machine with a front-panel display (i.e. model 95), you can use + "ibmmcascsi=display" to enable a drive activity indicator. + +3) 3c523 + drivers/net/3c523.c + drivers/net/3c523.h + 3Com 3c523 Etherlink/MC ethernet driver. + +4) SMC Ultra/MCA and IBM Adapter/A + drivers/net/smc-mca.c + drivers/net/smc-mca.h + Driver for the MCA version of the SMC Ultra and various other + OEM'ed and work-alike cards (Elite, Adapter/A, etc). + +5) NE/2 + driver/net/ne2.c + driver/net/ne2.h + The NE/2 is the MCA version of the NE2000. This may not work + with clones that have a different adapter id than the original + NE/2. + +6) Future Domain MCS-600/700, OEM'd IBM Fast SCSI Aapter/A and + Reply Sound Blaster/SCSI (SCSI part) + Better support for these cards than the driver for ISA. + Supports multiple cards with IRQ sharing. + +Also added boot time option of scsi-probe, which can do reordering of +SCSI host adapters. This will direct the kernel on the order which +SCSI adapter should be detected. Example: + scsi-probe=ibmmca,fd_mcs,adaptec1542,buslogic + +The serial drivers were modified to support the extended IO port range +of the typical MCA system (also #ifdef CONFIG_MCA). + +The following devices work with existing drivers: +1) Token-ring +2) Future Domain SCSI (MCS-600, MCS-700, not MCS-350, OEM'ed IBM SCSI) +3) Adaptec 1640 SCSI (using the aha1542 driver) +4) Bustek/Buslogic SCSI (various) +5) Probably all Arcnet cards. +6) Some, possibly all, MCA IDE controllers. +7) 3Com 3c529 (MCA version of 3c509) (patched) + +8) Intel EtherExpressMC (patched version) + You need to have CONFIG_MCA defined to have EtherExpressMC support. +9) Reply Sound Blaster/SCSI (SB part) (patched version) + +Bugs & Other Weirdness +====================== + +NMIs tend to occur with MCA machines because of various hardware +weirdness, bus timeouts, and many other non-critical things. Some basic +code to handle them (inspired by the NetBSD MCA code) has been added to +detect the guilty device, but it's pretty incomplete. If NMIs are a +persistent problem (on some model 70 or 80s, they occur every couple +shell commands), the CONFIG_IGNORE_NMI flag will take care of that. + +Various Pentium machines have had serious problems with the FPU test in +bugs.h. Basically, the machine hangs after the HLT test. This occurs, +as far as we know, on the Pentium-equipped 85s, 95s, and some PC Servers. +The PCI/MCA PC 750s are fine as far as I can tell. The ``mca-pentium'' +boot-prompt flag will disable the FPU bug check if this is a problem +with your machine. + +The model 80 has a raft of problems that are just too weird and unique +to get into here. Some people have no trouble while others have nothing +but problems. I'd suspect some problems are related to the age of the +average 80 and accompanying hardware deterioration, although others +are definitely design problems with the hardware. Among the problems +include SCSI controller problems, ESDI controller problems, and serious +screw-ups in the floppy controller. Oh, and the parallel port is also +pretty flaky. There were about 5 or 6 different model 80 motherboards +produced to fix various obscure problems. As far as I know, it's pretty +much impossible to tell which bugs a particular model 80 has (other than +triggering them, that is). + +Drivers are required for some MCA memory adapters. If you're suddenly +short a few megs of RAM, this might be the reason. The (I think) Enhanced +Memory Adapter commonly found on the model 70 is one. There's a very +alpha driver floating around, but it's pretty ugly (disassembled from +the DOS driver, actually). See the MCA Linux web page (URL below) +for more current memory info. + +The Thinkpad 700 and 720 will work, but various components are either +non-functional, flaky, or we don't know anything about them. The +graphics controller is supposed to be some WD, but we can't get things +working properly. The PCMCIA slots don't seem to work. Ditto for APM. +The serial ports work, but detection seems to be flaky. + +Credits +======= +A whole pile of people have contributed to the MCA code. I'd include +their names here, but I don't have a list handy. Check the MCA Linux +home page (URL below) for a perpetually out-of-date list. + +===================================================================== +MCA Linux Home Page: http://glycerine.itsmm.uni.edu/mca/ + +Christophe Beauregard +chrisb@truespectra.com +cpbeaure@calum.csclub.uwaterloo.ca + +===================================================================== +Appendix A: Sample /proc/mca + +This is from my model 8595. Slot 1 contains the standard IBM SCSI +adapter, slot 3 is an Adaptec AHA-1640, slot 5 is a XGA-1 video adapter, +and slot 7 is the 3c523 Etherlink/MC. + +/proc/mca/machine: +Model Id: 0xf8 +Submodel Id: 0x14 +BIOS Revision: 0x5 + +/proc/mca/pos: +Slot 1: ff 8e f1 fc a0 ff ff ff IBM SCSI Adapter w/Cache +Slot 2: ff ff ff ff ff ff ff ff +Slot 3: 1f 0f 81 3b bf b6 ff ff +Slot 4: ff ff ff ff ff ff ff ff +Slot 5: db 8f 1d 5e fd c0 00 00 +Slot 6: ff ff ff ff ff ff ff ff +Slot 7: 42 60 ff 08 ff ff ff ff 3Com 3c523 Etherlink/MC +Slot 8: ff ff ff ff ff ff ff ff +Video: ff ff ff ff ff ff ff ff +SCSI: ff ff ff ff ff ff ff ff + +/proc/mca/slot1: +Slot: 1 +Adapter Name: IBM SCSI Adapter w/Cache +Id: 8eff +Enabled: Yes +POS: ff 8e f1 fc a0 ff ff ff +Subsystem PUN: 7 +Detected at boot: Yes + +/proc/mca/slot3: +Slot: 3 +Adapter Name: Unknown +Id: 0f1f +Enabled: Yes +POS: 1f 0f 81 3b bf b6 ff ff + +/proc/mca/slot5: +Slot: 5 +Adapter Name: Unknown +Id: 8fdb +Enabled: Yes +POS: db 8f 1d 5e fd c0 00 00 + +/proc/mca/slot7: +Slot: 7 +Adapter Name: 3Com 3c523 Etherlink/MC +Id: 6042 +Enabled: Yes +POS: 42 60 ff 08 ff ff ff ff +Revision: 0xe +IRQ: 9 +IO Address: 0x3300-0x3308 +Memory: 0xd8000-0xdbfff +Transceiver: External +Device: eth0 +Hardware Address: 02 60 8c 45 c4 2a diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/boot/setup.S linux-2.0.35-mca/arch/i386/boot/setup.S --- linux/arch/i386/boot/setup.S Sat Mar 30 13:58:57 1996 +++ linux-2.0.35-mca/arch/i386/boot/setup.S Sat Aug 8 22:20:46 1998 @@ -294,6 +294,32 @@ stosb is_disk1: +! check for Micro Channel (MCA) bus + mov ax,cs ! aka #SETUPSEG + sub ax,#DELTA_INITSEG ! aka #INITSEG + mov ds,ax + mov ds,ax + xor ax,ax + mov [0x220], ax ! set table length to 0 + mov ah, #0xc0 + stc + int 0x15 ! puts feature table at es:bx + jc no_mca + push ds + mov ax,es + mov ds,ax + mov ax,cs ! aka #SETUPSEG + sub ax, #DELTA_INITSEG ! aka #INITSEG + mov es,ax + mov si,bx + mov di,#0x220 + mov cx,(si) + add cx,#2 ! table length is a short + rep + movsb + pop ds + +no_mca: ! Check for PS/2 pointing device mov ax,cs ! aka #SETUPSEG diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/config.in linux-2.0.35-mca/arch/i386/config.in --- linux/arch/i386/config.in Mon May 13 00:17:23 1996 +++ linux-2.0.35-mca/arch/i386/config.in Sat Aug 8 22:20:46 1998 @@ -30,6 +30,7 @@ bool ' PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE fi fi +bool 'MCA bus support' CONFIG_MCA bool 'System V IPC' CONFIG_SYSVIPC tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/Makefile linux-2.0.35-mca/arch/i386/kernel/Makefile --- linux/arch/i386/kernel/Makefile Tue Sep 16 17:33:59 1997 +++ linux-2.0.35-mca/arch/i386/kernel/Makefile Sat Aug 8 22:20:46 1998 @@ -24,6 +24,10 @@ O_OBJS := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \ ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o ksyms.o +ifdef CONFIG_MCA +O_OBJS += mca.o +endif + ifdef SMP O_OBJS += smp.o diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/ksyms.c linux-2.0.35-mca/arch/i386/kernel/ksyms.c --- linux/arch/i386/kernel/ksyms.c Sat Aug 8 21:35:45 1998 +++ linux-2.0.35-mca/arch/i386/kernel/ksyms.c Sat Aug 8 22:20:46 1998 @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -28,6 +30,19 @@ X(kernel_counter), X(active_kernel_processor), X(smp_invalidate_needed), +#endif +#ifdef CONFIG_MCA + /* Adapter probing and info methods. */ + X(mca_find_adapter), + X(mca_find_unused_adapter), + X(mca_write_pos), + X(mca_read_pos), + X(mca_read_stored_pos), + X(mca_set_adapter_name), + X(mca_get_adapter_name), + X(mca_set_adapter_procfn), + X(mca_isenabled), + X(mca_isadapter), #endif #include }; diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/mca.c linux-2.0.35-mca/arch/i386/kernel/mca.c --- linux/arch/i386/kernel/mca.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/arch/i386/kernel/mca.c Sat Aug 8 22:20:46 1998 @@ -0,0 +1,739 @@ +/* + * linux/arch/i386/kernel/mca.c + * Written by Martin Kolinek, February 1996 + * + * Changes: + * July 28, 1996: fixed up integrated SCSI detection. Chris Beauregard + * August 3rd, 1996: made mca_info local, made integrated registers + * accessible through standard function calls, added name field, + * more sanity checking. Chris Beauregard + * August 9, 1996: Rewrote /proc/mca. cpbeaure + * January 7, 1997: Added some basic NMI processing, added more + * information to mca_info structure. - cpbeaure +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* This structure holds MCA information. Each (plug-in) adapter has + * eight POS registers. Then the machine may have integrated video and + * SCSI subsystems, which also have eight POS registers. + * Other miscellaneous information follows. +*/ + +typedef enum { + MCA_ADAPTER_NORMAL = 0, + MCA_ADAPTER_NONE = 1, + MCA_ADAPTER_DISABLED = 2, + MCA_ADAPTER_ERROR = 3 +} MCA_AdapterStatus; + +struct MCA_adapter { + MCA_AdapterStatus status; /* is there a valid adapter? */ + int id; /* adapter id value */ + unsigned char pos[8]; /* POS registers */ + char name[48]; /* name of the device - provided by driver */ + char procname[8]; /* name of /proc/mca file */ + MCA_ProcFn procfn; /* /proc info callback */ + void* dev; /* device/context info for callback */ +}; + +struct MCA_info { + /* one for each of the 8 possible slots, plus one for integrated SCSI + and one for integrated video. */ + struct MCA_adapter slot[MCA_NUMADAPTERS]; + + /* two potential addresses for integrated SCSI adapter - this will + track which one we think it is */ + unsigned char which_scsi; +}; + +/* The mca_info structure pointer. If MCA bus is present, the function + * mca_probe() is invoked. The function puts motherboard, then all + * adapters into setup mode, allocates and fills an MCA_info structure, + * and points this pointer to the structure. Otherwise the pointer + * is set to zero. +*/ +static struct MCA_info* mca_info = 0; + +/*MCA registers*/ +#define MCA_MOTHERBOARD_SETUP_REG 0x94 +#define MCA_ADAPTER_SETUP_REG 0x96 +#define MCA_POS_REG(n) (0x100+(n)) + +#define MCA_ENABLED 0x01 /* POS 2, set if adapter enabled */ + +/*--------------------------------------------------------------------*/ + +#ifdef CONFIG_PROC_FS +static long mca_do_proc_init( long memory_start, long memory_end ); +static int mca_default_procfn( char* buf, int slot ); + +static int proc_mca_read( struct inode*, struct file*, char* buf, int count ); +static struct file_operations proc_mca_operations = { + NULL, proc_mca_read, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; +static struct inode_operations proc_mca_inode_operations = { + &proc_mca_operations, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; +#endif + +/*--------------------------------------------------------------------*/ +/* + Build the status info for the adapter. +*/ +static void +mca_configure_adapter_status( int slot ) { + mca_info->slot[slot].status = MCA_ADAPTER_NONE; + + mca_info->slot[slot].id = mca_info->slot[slot].pos[0] + + (mca_info->slot[slot].pos[1] << 8); + + if( !mca_info->slot[slot].id ) { + /* id = 0x0000 means the slot has some kind of hardware error */ + mca_info->slot[slot].status = MCA_ADAPTER_ERROR; + /* my 9556 has 0x0000 as its id! (zpg@castle.net) */ + /* + return; + */ + } else if( mca_info->slot[slot].id != 0xffff ) { + /* id non-zero and not 0xffff means there's an adapter */ + mca_info->slot[slot].status = MCA_ADAPTER_NORMAL; + } + + /* some integrated adapters may have 0xffff as a id, but still be + there and valid. Examples are on-board VGA of the 55SX and the + integrated SCSI of the 56, 57, and I think the 95 Ultimedia */ + + /* my 9556 has 0x0000 as its id! */ + if( (mca_info->slot[slot].id == 0xffff || + mca_info->slot[slot].id == 0x0000 ) && slot >= MCA_MAX_SLOT_NR ) { + int j; + + for( j = 2; j < 8; j ++ ) { + if( mca_info->slot[slot].pos[j] != 0xff ) { + mca_info->slot[slot].status = MCA_ADAPTER_NORMAL; + break; + } + } + } + + if( ! (mca_info->slot[slot].pos[2] & MCA_ENABLED) ) { + /* enable bit is in pos 2 */ + mca_info->slot[slot].status = MCA_ADAPTER_DISABLED; + } +} /* mca_configure_adapter_status */ + +/*--------------------------------------------------------------------*/ + +long mca_init(long memory_start, long memory_end) +{ + unsigned int i, j; + + /* WARNING: Be careful when making changes here. Putting an adapter + * and the motherboard simultaneously into setup mode may result in + * damage to chips (according to The Indispensible PC Hardware Book + * by Hans-Peter Messmer). Also, we disable system interrupts (so + * that we are not disturbed in the middle of this). + */ + + /*make sure the MCA bus is present*/ + if (!MCA_bus) return memory_start; + printk( "Micro Channel bus detected.\n" ); + cli(); + + /*allocate MCA_info structure (at address divisible by 8)*/ + if( ((memory_start+7)&(~7)) > memory_end ) { + /* uh oh */ + return memory_start; + } + + mca_info = (struct MCA_info*) ((memory_start+7)&(~7)); + memory_start = ((long)mca_info) + sizeof(struct MCA_info); + + /*make sure adapter setup is off*/ + outb_p(0, MCA_ADAPTER_SETUP_REG); + + /* Put motherboard into video setup mode, read integrated video + * pos registers, and turn motherboard setup off. + */ + outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG); + mca_info->slot[MCA_INTEGVIDEO].name[0] = 0; + for (j=0; j<8; j++) { + mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j)); + } + mca_configure_adapter_status( MCA_INTEGVIDEO ); + + /* Put motherboard into scsi setup mode, read integrated scsi + * pos registers, and turn motherboard setup off. + * + * It seems there are two possible SCSI registers. Martin says that + * for the 56,57, 0xf7 is the one, but fails on the 76. + * Alfredo (apena@vnet.ibm.com) says + * 0xfd works on his machine. We'll try both of them. I figure it's + * a good bet that only one could be valid at a time. This could + * screw up though if one is used for something else on the other + * machine. + */ + outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG); + mca_info->slot[MCA_INTEGSCSI].name[0] = 0; + mca_info->which_scsi = 0; + for (j=0; j<8; j++) { + if( (mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff ) { + /* 0xff all across means no device. 0x00 means + something's broken, but a device is probably there. + However, if you get 0x00 from a motherboard register, + you're fucked and it doesn't matter what we do with the + result. For the record, on the 57SLC, the integrated + SCSI adapter has 0xffff for the adapter ID, but nonzero + for other registers. */ + + mca_info->which_scsi = 0xf7; + } + } + if( !mca_info->which_scsi ) { + /* didn't find it at 0xfd, try somewhere else... */ + mca_info->which_scsi = 0xfd; + outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG); + for (j=0; j<8; j++) { + mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j)); + } + } + mca_configure_adapter_status( MCA_INTEGSCSI ); + + /* turn off motherboard setup */ + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + + /* Now loop over MCA slots: put each adapter into setup mode, and + * read its pos registers. Then put adapter setup off. + */ + for (i=0; islot[i].pos[j]=inb_p(MCA_POS_REG(j)); + } + mca_info->slot[i].name[0] = 0; + mca_configure_adapter_status( i ); + } + outb_p(0, MCA_ADAPTER_SETUP_REG); + + /*enable interrupts and return memory start*/ + sti(); + + request_region(0x60,0x01,"system control port B (MCA)"); + request_region(0x90,0x01,"arbitration (MCA)"); + request_region(0x91,0x01,"card Select Feedback (MCA)"); + request_region(0x92,0x01,"system Control port A (MCA)"); + request_region(0x94,0x01,"system board setup (MCA)"); + request_region(0x96,0x02,"POS (MCA)"); + request_region(0x100,0x08,"POS (MCA)"); + +#ifdef CONFIG_PROC_FS + memory_start = mca_do_proc_init( memory_start, memory_end ); +#endif + + return memory_start; +} + +/*--------------------------------------------------------------------*/ +static void +mca_handle_nmi_slot( int slot, int check_flag ) { + if( slot < MCA_MAX_SLOT_NR ) { + printk( "NMI: caused by MCA adapter in slot %d (%s)\n", slot + 1, + mca_info->slot[slot].name ); + } else if( slot == MCA_INTEGSCSI ) { + printk( "NMI: caused by MCA integrated SCSI adapter (%s)\n", + mca_info->slot[slot].name ); + } else if( slot == MCA_INTEGVIDEO ) { + printk( "NMI: caused by MCA integrated video adapter (%s)\n", + mca_info->slot[slot].name ); + } + + /* more info available in pos 6 and 7? */ + if( check_flag ) { + unsigned char pos6, pos7; + + pos6 = mca_read_pos( slot, 6 ); + pos7 = mca_read_pos( slot, 7 ); + + printk( "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7 ); + } + +} /* mca_handle_nmi_slot */ + +/*--------------------------------------------------------------------*/ +void +mca_handle_nmi( void ) { + int i; + unsigned char pos5; + + /* First try - scan the various adapters and see if a specific + adapter was responsible for the error */ + for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) { + + /* bit 7 of POS 5 is reset when this adapter has a hardware + error. bit 7 it reset if there's error information available + in pos 6 and 7. */ + + pos5 = mca_read_pos( i, 5 ); + + if( !(pos5 & 0x80) ) { + mca_handle_nmi_slot( i, !(pos5 & 0x40) ); + return; + } + } + + /* if I recall correctly, there's a whole bunch of other things that + we can do to check for NMI problems, but that's all I know about at + the moment. */ + printk( "NMI generated from unknown source!\n" ); +} /* mca_handle_nmi */ + +/*--------------------------------------------------------------------*/ +int +mca_find_adapter( int id, int start ) { + if( mca_info == 0 || id == 0 || id == 0xffff ) { + return MCA_NOTFOUND; + } + + for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) { + /* There's no point in returning + adapters that aren't enabled, since they can't actually + be used. However, they might be needed for statistical + purposes or something... But in that case, the user is + free to iterate through the devices manually. */ + + if( mca_info->slot[start].status == MCA_ADAPTER_DISABLED ) { + continue; + } + + if( id == mca_info->slot[start].id ) { + return start; + } + } + + return MCA_NOTFOUND; +} /* mca_find_adapter() */ + +/*--------------------------------------------------------------------*/ +/* this will only find any adapter that's not used/claimed previously */ +/* which is signaled by the presence of adapter name */ +/* all mca adapter drivers MUST populate the name to claim the card */ +/* this code is to facilitate the use of multiple same kind cards. */ +/* cloned from mca_find_adapter by ZP Gu (zpg@castle.net) */ +int +mca_find_unused_adapter( int id, int start ) { + if( mca_info == 0 || id == 0 || id == 0xffff ) { + return MCA_NOTFOUND; + } + + for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) { + /* There's no point in returning + adapters that aren't enabled, since they can't actually + be used. However, they might be needed for statistical + purposes or something... But in that case, the user is + free to iterate through the devices manually. */ + + if( mca_info->slot[start].status == MCA_ADAPTER_DISABLED || + mca_info->slot[start].name[0] != '\0') { + continue; + } + + if( id == mca_info->slot[start].id ) { + return start; + } + } + + return MCA_NOTFOUND; +} /* mca_find_unused_adapter() */ + +/*--------------------------------------------------------------------*/ +unsigned char +mca_read_stored_pos( int slot, int reg ) { + if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0; + if( reg < 0 || reg >= 8 ) return 0; + return mca_info->slot[slot].pos[reg]; +} /* mca_read_stored_pos() */ + +/*--------------------------------------------------------------------*/ +unsigned char +mca_read_pos( int slot, int reg ) { + unsigned int byte = 0; + + if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0; + if( reg < 0 || reg >= 8 ) return 0; + + cli(); + + if( slot == MCA_INTEGSCSI && mca_info->which_scsi ) { + /* disable adapter setup, enable motherboard setup */ + outb_p(0, MCA_ADAPTER_SETUP_REG); + outb_p(mca_info->which_scsi, MCA_MOTHERBOARD_SETUP_REG); + + byte = inb_p(MCA_POS_REG(reg)); + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + } else if( slot == MCA_INTEGVIDEO ) { + /* disable adapter setup, enable motherboard setup */ + outb_p(0, MCA_ADAPTER_SETUP_REG); + outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG); + + byte = inb_p(MCA_POS_REG(reg)); + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + } else if( slot < MCA_MAX_SLOT_NR ) { + /*make sure motherboard setup is off*/ + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + + /* read in the appropriate register */ + outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG); + byte = inb_p(MCA_POS_REG(reg)); + outb_p(0, MCA_ADAPTER_SETUP_REG); + } + + /* make sure the stored values are consistent, while we're here */ + mca_info->slot[slot].pos[reg] = byte; + + sti(); + + return byte; +} /* mca_read_pos() */ + +/*--------------------------------------------------------------------*/ +/* Note that this a technically a Bad Thing, as IBM tech stuff says + you should only set POS values through their utilities. + However, some devices such as the 3c523 recommend that you write + back some data to make sure the configuration is consistent. + I'd say that IBM is right, but I like my drivers to work. + This function can't do checks to see if multiple devices end up + with the same resources, so you might see magic smoke if someone + screws up. */ +void +mca_write_pos( int slot, int reg, unsigned char byte ) { + if( slot < 0 || slot >= MCA_MAX_SLOT_NR ) return; + if( reg < 0 || reg >= 8 ) return; + if (mca_info == 0 ) return; + + cli(); + + /*make sure motherboard setup is off*/ + outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG); + + /* read in the appropriate register */ + outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG); + outb_p( byte, MCA_POS_REG(reg) ); + outb_p(0, MCA_ADAPTER_SETUP_REG); + + sti(); + + /* update the global register list, while we have the byte */ + mca_info->slot[slot].pos[reg] = byte; +} /* mca_write_pos() */ + +/*--------------------------------------------------------------------*/ +void +mca_set_adapter_name( int slot, char* name ) { + if( mca_info == 0 ) return; + + if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + if( name != NULL ) { + strncpy( mca_info->slot[slot].name, name, + sizeof(mca_info->slot[slot].name)-1 ); + mca_info->slot[slot].name[ + sizeof(mca_info->slot[slot].name)-1] = 0; + } else { + mca_info->slot[slot].name[0] = 0; + } + } +} + +void +mca_set_adapter_procfn( int slot, MCA_ProcFn procfn, void* dev) +{ + if( mca_info == 0 ) return; + + if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + mca_info->slot[slot].procfn = procfn; + mca_info->slot[slot].dev = dev; + } +} + +char* +mca_get_adapter_name( int slot ) { + if( mca_info == 0 ) return 0; + + if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + return mca_info->slot[slot].name; + } + + return 0; +} + +int mca_isadapter( int slot ) +{ + if( mca_info == 0 ) return 0; + + if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + return (( mca_info->slot[slot].status == MCA_ADAPTER_NORMAL ) + || (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED ) ); + } + + return 0; +} + +int mca_isenabled( int slot ) +{ + if( mca_info == 0 ) return 0; + + if( slot >= 0 && slot < MCA_NUMADAPTERS ) { + return( mca_info->slot[slot].status == MCA_ADAPTER_NORMAL ); + } + + return 0; +} +/*--------------------------------------------------------------------*/ + +#ifdef CONFIG_PROC_FS + +int get_mca_info(char *buf) +{ + int i, j, len = 0; + + if( MCA_bus && mca_info != 0 ) { + /*format pos registers of eight MCA slots*/ + for (i=0; islot[i].pos[j]); + len += sprintf( buf+len, " %s\n", mca_info->slot[i].name ); + } + + /*format pos registers of integrated video subsystem*/ + len += sprintf(buf+len, "Video: "); + for (j=0; j<8; j++) + len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]); + len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name ); + + /*format pos registers of integrated SCSI subsystem*/ + len += sprintf(buf+len, "SCSI: "); + for (j=0; j<8; j++) + len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]); + len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name ); + } else { + /* leave it empty if MCA not detected */ + /* this should never happen */ + } + + return len; +} + +/*--------------------------------------------------------------------*/ +long +mca_do_proc_init( long memory_start, long memory_end ) +{ + int i = 0; + struct proc_dir_entry* node = 0; + + if( mca_info == 0 ) return memory_start; /* never happens */ + + proc_register( &proc_mca, &(struct proc_dir_entry) { + PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO, + 1, 0, 0, 0, &proc_mca_inode_operations,} ); + + proc_register( &proc_mca, &(struct proc_dir_entry) { + PROC_MCA_MACHINE, 7, "machine", S_IFREG|S_IRUGO, + 1, 0, 0, 0, &proc_mca_inode_operations,} ); + + /* initialize /proc entries for existing adapters */ + for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) { + mca_info->slot[i].procfn = 0; + mca_info->slot[i].dev = 0; + + if( ! mca_isadapter( i ) ) continue; + if( memory_start + sizeof(struct proc_dir_entry) > memory_end ) { + continue; + } + node = (struct proc_dir_entry*) memory_start; + memory_start += sizeof(struct proc_dir_entry); + + if( i < MCA_MAX_SLOT_NR ) { + node->low_ino = PROC_MCA_SLOT + i; + node->namelen = sprintf( mca_info->slot[i].procname, + "slot%d", i+1 ); + } else if( i == MCA_INTEGVIDEO ) { + node->low_ino = PROC_MCA_VIDEO; + node->namelen = sprintf( mca_info->slot[i].procname, + "video" ); + } else if( i == MCA_INTEGSCSI ) { + node->low_ino = PROC_MCA_SCSI; + node->namelen = sprintf( mca_info->slot[i].procname, + "scsi" ); + } + node->name = mca_info->slot[i].procname; + node->mode = S_IFREG | S_IRUGO; + node->ops = &proc_mca_inode_operations; + proc_register( &proc_mca, node ); + } + + return memory_start; +} /* mca_do_proc_init() */ + +/*--------------------------------------------------------------------*/ +int +mca_default_procfn( char* buf, int slot ) { + int len = 0, i; + + /* this really shouldn't happen... */ + if( mca_info == 0 ) { + *buf = 0; + return 0; + } + + /* print out the basic information */ + + if( slot < MCA_MAX_SLOT_NR ) { + len += sprintf( buf+len, "Slot: %d\n", slot+1 ); + } else if( slot == MCA_INTEGSCSI ) { + len += sprintf( buf+len, "Integrated SCSI Adapter\n" ); + } else if( slot == MCA_INTEGVIDEO ) { + len += sprintf( buf+len, "Integrated Video Adapter\n" ); + } + if( mca_info->slot[slot].name[0] ) { + /* drivers might register a name without /proc handler... */ + len += sprintf( buf+len, "Adapter Name: %s\n", + mca_info->slot[slot].name ); + } else { + len += sprintf( buf+len, "Adapter Name: Unknown\n" ); + } + len += sprintf( buf+len, "Id: %02x%02x\n", + mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0] ); + len += sprintf( buf+len, "Enabled: %s\nPOS: ", + mca_isenabled(slot) ? "Yes" : "No" ); + for (i=0; i<8; i++) { + len += sprintf(buf+len, "%02x ", mca_info->slot[slot].pos[i]); + } + buf[len++] = '\n'; + buf[len] = 0; + + return len; +} /* mca_default_procfn() */ + +static int +get_mca_machine_info( char* buf ) { + int len = 0; + + len += sprintf( buf+len, "Model Id: 0x%x\n", machine_id ); + len += sprintf( buf+len, "Submodel Id: 0x%x\n", machine_submodel_id ); + len += sprintf( buf+len, "BIOS Revision: 0x%x\n", BIOS_revision ); + + return len; +} + +/* +static int +mca_not_implemented( char* buf ) { + return sprintf( buf, "Sorry, not implemented yet...\n" ); +} +*/ + +static int mca_fill( char* page, int pid, int type, char** start, + off_t offset, int length +) { + int len = 0; + int slot = 0; + + switch( type ) { + case PROC_MCA_REGISTERS: + return get_mca_info( page ); + case PROC_MCA_MACHINE: + return get_mca_machine_info( page ); + case PROC_MCA_VIDEO: + slot = MCA_INTEGVIDEO; + break; + case PROC_MCA_SCSI: + slot = MCA_INTEGSCSI; + break; + default: + if( type < PROC_MCA_SLOT || type >= PROC_MCA_LAST ) { + return -EBADF; + } + slot = type - PROC_MCA_SLOT; + break; + } + + /* if we made it here, we better have a valid slot */ + + /* get the standard info */ + len = mca_default_procfn( page, slot ); + + /* do any device-specific processing, if there is any */ + if( mca_info->slot[slot].procfn ) { + len += mca_info->slot[slot].procfn( page+len, slot, + mca_info->slot[slot].dev ); + } + + return len; +} /* mca_fill() */ + +/* blatantly stolen from fs/proc/array.c, and thus is probably overkill */ +#define PROC_BLOCK_SIZE (3*1024) +int proc_mca_read( struct inode* inode, struct file* file, + char* buf, int count +) { + unsigned long page; + char *start; + int length; + int end; + unsigned int type, pid; + struct proc_dir_entry *dp; + + if (count < 0) + return -EINVAL; + if (count > PROC_BLOCK_SIZE) + count = PROC_BLOCK_SIZE; + if (!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + type = inode->i_ino; + pid = type >> 16; + type &= 0x0000ffff; + start = 0; + dp = (struct proc_dir_entry *) inode->u.generic_ip; + length = mca_fill((char *) page, pid, type, + &start, file->f_pos, count); + if (length < 0) { + free_page(page); + return length; + } + if (start != 0) { + /* We have had block-adjusting processing! */ + memcpy_tofs(buf, start, length); + file->f_pos += length; + count = length; + } else { + /* Static 4kB (or whatever) block capacity */ + if (file->f_pos >= length) { + free_page(page); + return 0; + } + if (count + file->f_pos > length) + count = length - file->f_pos; + end = count + file->f_pos; + memcpy_tofs(buf, (char *) page + file->f_pos, count); + file->f_pos = end; + } + free_page(page); + return count; +} /* proc_mca_read() */ + +#endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/setup.c linux-2.0.35-mca/arch/i386/kernel/setup.c --- linux/arch/i386/kernel/setup.c Sat Aug 8 21:35:45 1998 +++ linux-2.0.35-mca/arch/i386/kernel/setup.c Sat Aug 8 22:20:46 1998 @@ -45,6 +45,7 @@ int fdiv_bug = 0; /* set if Pentium(TM) with FP bug */ int pentium_f00f_bug = 0; /* set if Pentium(TM) with F00F bug */ int have_cpuid = 0; /* set if CPUID instruction works */ +int has_mca_pentium = 0; /* MCA with bad pentium processor */ char x86_vendor_id[13] = "unknown"; @@ -61,6 +62,12 @@ * Bus types .. */ int EISA_bus = 0; +int MCA_bus = 0; + +/* for MCA, but anyone else can use it if they want */ +unsigned int machine_id = 0; +unsigned int machine_submodel_id = 0; +unsigned int BIOS_revision = 0; /* * Setup options @@ -70,6 +77,10 @@ #ifdef CONFIG_APM struct apm_bios_info apm_bios_info; #endif +struct sys_desc_table_struct { + unsigned short length; + unsigned char table[0]; +}; unsigned char aux_device_present; @@ -102,6 +113,7 @@ #define KERNEL_START (*(unsigned long *) (PARAM+0x214)) #define INITRD_START (*(unsigned long *) (PARAM+0x218)) #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) +#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0x220)) #define COMMAND_LINE ((char *) (PARAM+2048)) #define COMMAND_LINE_SIZE 256 @@ -132,6 +144,12 @@ #ifdef CONFIG_APM apm_bios_info = APM_BIOS_INFO; #endif + if( SYS_DESC_TABLE.length != 0 ) { + MCA_bus = SYS_DESC_TABLE.table[3] &0x2; + machine_id = SYS_DESC_TABLE.table[0]; + machine_submodel_id = SYS_DESC_TABLE.table[1]; + BIOS_revision = SYS_DESC_TABLE.table[2]; + } aux_device_present = AUX_DEVICE_INFO; memory_end = (1<<20) + (EXT_MEM_K<<10); memory_end &= PAGE_MASK; @@ -206,7 +224,7 @@ /* request io space for devices used on all i[345]86 PC'S */ request_region(0x00,0x20,"dma1"); request_region(0x40,0x20,"timer"); - request_region(0x80,0x20,"dma page reg"); + request_region(0x80,0x10,"dma page reg"); request_region(0xc0,0x20,"dma2"); request_region(0xf0,0x10,"npu"); } diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/time.c linux-2.0.35-mca/arch/i386/kernel/time.c --- linux/arch/i386/kernel/time.c Sat Aug 8 21:30:05 1998 +++ linux-2.0.35-mca/arch/i386/kernel/time.c Sat Aug 8 22:20:46 1998 @@ -375,6 +375,21 @@ closely for now.. */ /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ +#ifdef CONFIG_MCA + if( MCA_bus ) { + /* The PS/2 uses level-triggered interrupts. You can't + turn them off, nor would you want to (any attempt to + enable edge-triggered interrupts usually gets intercepted by a + special hardware circuit). Hence we have to acknowledge + the timer interrupt. Through some incredibly stupid + design idea, the reset for IRQ 0 is done by setting the + high bit of the PPI port B (0x61). Note that some PS/2s, + notably the 55SX, work fine if this is removed. */ + + irq = inb_p( 0x61 ); /* read the current state */ + outb_p( irq|0x80, 0x61 ); /* reset the IRQ */ + } +#endif } #ifndef CONFIG_APM /* cycle counter may be unreliable */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/arch/i386/kernel/traps.c linux-2.0.35-mca/arch/i386/kernel/traps.c --- linux/arch/i386/kernel/traps.c Sat Aug 8 21:35:45 1998 +++ linux-2.0.35-mca/arch/i386/kernel/traps.c Sat Aug 8 22:20:46 1998 @@ -20,6 +20,10 @@ #include #include #include +#ifdef CONFIG_MCA +#include +#include +#endif #include #include @@ -233,6 +237,13 @@ smp_flush_tlb_rcv(); #else #ifndef CONFIG_IGNORE_NMI +#ifdef CONFIG_MCA + /* Might actually be able to figure out what the guilty party is. */ + if( MCA_bus ) { + mca_handle_nmi(); + return; + } +#endif printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); printk("You probably have a hardware problem with your RAM chips or a\n"); printk("power saving mode enabled.\n"); diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/block/Config.in linux-2.0.35-mca/drivers/block/Config.in --- linux/drivers/block/Config.in Sat Aug 8 21:35:46 1998 +++ linux-2.0.35-mca/drivers/block/Config.in Sat Aug 8 22:20:46 1998 @@ -37,6 +37,9 @@ bool ' UMC 8672 support' CONFIG_BLK_DEV_UMC8672 fi fi +if [ "$CONFIG_MCA" = "y" ]; then + bool 'PS/2 ESDI harddisk support' CONFIG_BLK_DEV_PS2 +fi comment 'Additional Block Devices' diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/block/Makefile linux-2.0.35-mca/drivers/block/Makefile --- linux/drivers/block/Makefile Sat Aug 8 21:35:46 1998 +++ linux-2.0.35-mca/drivers/block/Makefile Sat Aug 8 22:20:46 1998 @@ -65,6 +65,11 @@ L_OBJS += triton.o endif +ifeq ($(CONFIG_BLK_DEV_PS2),y) +L_OBJS += ps2esdi.o +endif + + ifeq ($(CONFIG_BLK_DEV_DTC2278),y) L_OBJS += dtc2278.o endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/block/floppy.c linux-2.0.35-mca/drivers/block/floppy.c --- linux/drivers/block/floppy.c Sat Aug 8 21:30:05 1998 +++ linux-2.0.35-mca/drivers/block/floppy.c Sun Aug 9 11:49:44 1998 @@ -164,6 +164,10 @@ #include #include +#ifdef CONFIG_MCA +static int slow_floppy = 0; +#endif + static int use_virtual_dma=0; /* virtual DMA for Intel */ static unsigned short virtual_dma_port=0x3f0; void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs); @@ -1265,6 +1269,11 @@ /* Convert step rate from microseconds to milliseconds and 4 bits */ srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR; +#ifdef CONFIG_MCA + if( slow_floppy ) { + srt = srt / 4; + } +#endif SUPBOUND(srt, 0xf); INFBOUND(srt, 0); @@ -3920,7 +3929,18 @@ { int i; int param; - if (str) + if (str) { +#ifdef CONFIG_MCA + /* + PS/2 floppies have much slower step rates than regular floppies. + It's been recommended that take about 1/4 of the default speed + in some more extreme cases. + */ + if( strcmp(str,"slow") == 0) { + slow_floppy = 1; + return; + } +#endif for (i=0; i< ARRAY_SIZE(config_params); i++){ if (strcmp(str,config_params[i].name) == 0){ if (ints[0]) @@ -3936,6 +3956,7 @@ return; } } + } if (str) { DPRINT("unknown floppy option [%s]\n", str); diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/block/ll_rw_blk.c linux-2.0.35-mca/drivers/block/ll_rw_blk.c --- linux/drivers/block/ll_rw_blk.c Sat Aug 8 21:35:49 1998 +++ linux-2.0.35-mca/drivers/block/ll_rw_blk.c Sat Aug 8 22:20:46 1998 @@ -685,6 +685,9 @@ #ifdef CONFIG_BLK_DEV_HD hd_init(); #endif +#ifdef CONFIG_BLK_DEV_PS2 + ps2esdi_init(); +#endif #ifdef CONFIG_BLK_DEV_XD xd_init(); #endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/block/ps2esdi.c linux-2.0.35-mca/drivers/block/ps2esdi.c --- linux/drivers/block/ps2esdi.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/block/ps2esdi.c Sat Aug 8 22:20:46 1998 @@ -0,0 +1,1163 @@ +/* ps2esdi driver based on assembler code by Arindam Banerji, + written by Peter De Schrijver */ +/* Reassuring note to IBM : This driver was NOT developed by vice-versa + engineering the PS/2's BIOS */ +/* Dedicated to Wannes, Tofke, Ykke, Godot, Killroy and all those + other lovely fish out there... */ +/* This code was written during the long and boring WINA + elections 1994 */ +/* Thanks to Arindam Banerij for giving me the source of his driver */ +/* This code may be freely distributed and modified in any way, + as long as these notes remain intact */ + +/* Revised: 05/07/94 by Arindam Banerji (axb@cse.nd.edu) */ +/* Revised: 09/08/94 by Peter De Schrijver (stud11@cc4.kuleuven.ac.be) + Thanks to Arindam Banerij for sending me the docs of the adapter */ + +/* BA Modified for ThinkPad 720 by Boris Ashkinazi */ +/* (bash@vnet.ibm.com) 08/08/95 */ + +/* Modified further for ThinkPad-720C by Uri Blumenthal */ +/* (uri@watson.ibm.com) Sep 11, 1995 */ + +/* TODO : + + Timeouts + + Get disk parameters + + DMA above 16MB + + reset after read/write error + */ + +#include +#include + +#ifdef CONFIG_BLK_DEV_PS2 + +#define MAJOR_NR PS2ESDI_MAJOR + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PS2ESDI_IRQ 14 +#define MAX_HD 1 +#define MAX_RETRIES 5 +#define MAX_16BIT 65536 +#define ESDI_TIMEOUT 0xf000 +#define ESDI_STAT_TIMEOUT 4 + +#define TYPE_0_CMD_BLK_LENGTH 2 +#define TYPE_1_CMD_BLK_LENGTH 4 + + +static void reset_ctrl(void); + +int ps2esdi_init(void); + +static void ps2esdi_geninit(struct gendisk *ignored); + +static void do_ps2esdi_request(void); + +static void ps2esdi_readwrite(int cmd, u_char drive, u_int block, u_int count); + +static void ps2esdi_fill_cmd_block(u_short * cmd_blk, u_short cmd, +u_short cyl, u_short head, u_short sector, u_short length, u_char drive); + +static int ps2esdi_out_cmd_blk(u_short * cmd_blk); + +static void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode); + +static void ps2esdi_interrupt_handler(int irq, void *dev_id, + struct pt_regs *regs); +static void (*current_int_handler) (u_int) = NULL; +static void ps2esdi_normal_interrupt_handler(u_int); +static void ps2esdi_initial_reset_int_handler(u_int); +static void ps2esdi_geometry_int_handler(u_int); + +static void ps2esdi_continue_request(void); + +static int ps2esdi_open(struct inode *inode, struct file *file); + +static void ps2esdi_release(struct inode *inode, struct file *file); + +static int ps2esdi_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg); + +static int ps2esdi_reread_partitions(int dev); + +static int ps2esdi_read_status_words(int num_words, int max_words, u_short * buffer); + +static void dump_cmd_complete_status(u_int int_ret_code); + +static void ps2esdi_get_device_cfg(void); + +void ps2esdi_reset_timer(unsigned long unused); + +u_int dma_arb_level; /* DMA arbitration level */ + +static struct wait_queue *ps2esdi_int = NULL, *ps2esdi_wait_open = NULL; +int no_int_yet; +static int access_count[MAX_HD] = +{0,}; +static char ps2esdi_valid[MAX_HD] = +{0,}; +static int ps2esdi_sizes[MAX_HD << 6] = +{0,}; +static int ps2esdi_blocksizes[MAX_HD << 6] = +{0,}; +static int ps2esdi_drives = 0; +static struct hd_struct ps2esdi[MAX_HD << 6]; +static u_short io_base; +static struct timer_list esdi_timer = +{NULL, NULL, 0, 0L, ps2esdi_reset_timer}; +static int reset_status; +int tp720esdi = 0; /* Is it Integrated ESDI of ThinkPad-720? */ +int integrated_esdi = 0; /* Is it integrated ESDI, period? */ + +struct ps2esdi_i_struct { + unsigned int head, sect, cyl, wpcom, lzone, ctl; +}; + +#if 0 +#if 0 /* try both - I don't know which one is better... UB */ +struct ps2esdi_i_struct ps2esdi_info[] = +{ + {4, 48, 1553, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}}; +#else +struct ps2esdi_i_struct ps2esdi_info[] = +{ + {64, 32, 161, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}}; +#endif +#endif +struct ps2esdi_i_struct ps2esdi_info[] = +{ + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0}}; + +static struct file_operations ps2esdi_fops = +{ + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + ps2esdi_ioctl, /* ioctl */ + NULL, /* mmap */ + ps2esdi_open, /* open */ + ps2esdi_release, /* release */ + block_fsync /* fsync */ +}; + +static struct gendisk ps2esdi_gendisk = +{ + MAJOR_NR, /* Major number */ + "ed", /* Major name */ + 6, /* Bits to shift to get real from partition */ + 1 << 6, /* Number of partitions per real disk */ + MAX_HD, /* maximum number of real disks */ + ps2esdi_geninit, /* init function */ + ps2esdi, /* hd struct */ + ps2esdi_sizes, /* block sizes */ + 0, /* number */ + (void *) ps2esdi_info, /* internal */ + NULL /* next */ +}; + +/* initialization routine called by ll_rw_blk.c */ +int ps2esdi_init(void) +{ + + /* register the device - pass the name, major number and operations + vector . */ + if (register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) { + printk("%s: Unable to get major number %d\n", DEVICE_NAME, MAJOR_NR); + return -1; + } + /* set up some global information - indicating device specific info */ + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + + /* some minor housekeeping - setup the global gendisk structure */ + ps2esdi_gendisk.next = gendisk_head; + gendisk_head = &ps2esdi_gendisk; + return 0; + +} /* ps2esdi_init */ + +/* handles boot time command line parameters */ +void tp720_setup(char *str, int *ints) +{ + /* no params, just sets the tp720esdi flag if it exists */ + + printk("%s: TP 720 ESDI flag set\n", DEVICE_NAME); + tp720esdi = 1; + integrated_esdi = 1; +} + +void ed_setup(char *str, int *ints) +{ + int hdind = 0; + + /* handles 3 parameters only - corresponding to + 1. Number of cylinders + 2. Number of heads + 3. Sectors/track + */ + + if (ints[0] != 3) + return; + + /* set the index into device specific information table */ + if( str[3] == 0 ) { + if (ps2esdi_info[0].head != 0) + hdind = 1; + } else if( str[3] == 'a' ) { + hdind = 0; + } else if( str[3] == 'b' ) { + hdind = 1; + } else { + printk( "%s: unknown option%s\n", DEVICE_NAME, str ); + return; + } + + /* set up all the device information */ + ps2esdi_info[hdind].head = ints[2]; + ps2esdi_info[hdind].sect = ints[3]; + ps2esdi_info[hdind].cyl = ints[1]; + ps2esdi_info[hdind].wpcom = 0; + ps2esdi_info[hdind].lzone = ints[1]; + ps2esdi_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); + ps2esdi_drives = hdind + 1; +} /* ed_setup */ + +static int ps2esdi_getinfo(char *buf, int slot, void *d) +{ + int len = 0; + + len += sprintf(buf + len, "DMA Arbitration Level: %d\n", + dma_arb_level); + len += sprintf(buf + len, "IO Port: %x\n", io_base); + len += sprintf(buf + len, "IRQ: 14\n"); + len += sprintf(buf + len, "Drives: %d\n", ps2esdi_drives); + + return len; +} + +/* ps2 esdi specific initialization - called thru the gendisk chain */ +static void ps2esdi_geninit(struct gendisk *ignored) +{ + /* + + The first part contains the initialization code + for the ESDI disk subsystem. All we really do + is search for the POS registers of the controller + to do some simple setup operations. First, we + must ensure that the controller is installed, + enabled, and configured as PRIMARY. Then we must + determine the DMA arbitration level being used by + the controller so we can handle data transfer + operations properly. If all of this works, then + we will set the INIT_FLAG to a non-zero value. + */ + + int slot = 0, i, reset_start, reset_end; + u_char status; + unsigned short adapterID; + + if ((slot = mca_find_adapter(INTG_ESDI_ID, 0)) != MCA_NOTFOUND) { + adapterID = INTG_ESDI_ID; + printk("%s: integrated ESDI adapter found in slot %d\n", + DEVICE_NAME, slot+1); + mca_set_adapter_name(slot, "PS/2 Integrated ESDI"); + ps2esdi_drives = 1; /* only one drive with integrated ESDI */ + integrated_esdi = 1; + } else if ((slot = mca_find_adapter(NRML_ESDI_ID, 0)) != -1) { + adapterID = NRML_ESDI_ID; + printk("%s: normal ESDI adapter found in slot %d\n", + DEVICE_NAME, slot+1); + mca_set_adapter_name(slot, "PS/2 ESDI"); + } else { + return; + } + + mca_set_adapter_procfn(slot, (MCA_ProcFn) ps2esdi_getinfo, NULL); + + /* Found the slot - read the POS register 2 to get the necessary + configuration and status information. POS register 2 has the + following information : + Bit Function + 7 reserved = 0 + 6 arbitration method + 0 - fairness enabled + 1 - fairness disabled, linear priority assignment + 5-2 arbitration level + 1 alternate address + 1 alternate address + 0 - use addresses 0x3510 - 0x3517 + 0 adapter enable + */ + + status = mca_read_stored_pos(slot, 2); + /* is it enabled ? */ + if (!(status & STATUS_ENABLED)) { + printk("%s: ESDI adapter disabled\n", DEVICE_NAME); + return; + } + /* try to grab IRQ, and try to grab a slow IRQ if it fails, so we can + share with the SCSI driver */ + if (request_irq(PS2ESDI_IRQ, ps2esdi_interrupt_handler, + SA_INTERRUPT | SA_SHIRQ, "PS/2 ESDI", &ps2esdi_gendisk) + && request_irq(PS2ESDI_IRQ, ps2esdi_interrupt_handler, + SA_SHIRQ, "PS/2 ESDI", &ps2esdi_gendisk) + ) { + printk("%s: Unable to get IRQ %d\n", DEVICE_NAME, PS2ESDI_IRQ); + return; + } + if (status & STATUS_ALTERNATE) + io_base = ALT_IO_BASE; + else + io_base = PRIMARY_IO_BASE; + + /* get the dma arbitration level */ + dma_arb_level = (status >> 2) & 0xf; + + /* BA */ + printk("%s: DMA arbitration level : %d\n", + DEVICE_NAME, dma_arb_level); + + LITE_ON; + current_int_handler = ps2esdi_initial_reset_int_handler; + reset_ctrl(); + reset_status = 0; + reset_start = jiffies; + while (!reset_status) { + esdi_timer.expires = 100; + esdi_timer.data = 0; + esdi_timer.next = esdi_timer.prev = NULL; + add_timer(&esdi_timer); + sleep_on(&ps2esdi_int); + } + reset_end = jiffies; + LITE_OFF; + /* + printk("%s: reset interrupt after %d jiffies, %u.%02u secs\n", + DEVICE_NAME, reset_end - reset_start, (reset_end - reset_start) / HZ, + (reset_end - reset_start) % HZ); + */ + + /* finally this part sets up some global data structures etc. */ + + ps2esdi_get_device_cfg(); + + current_int_handler = ps2esdi_normal_interrupt_handler; + + ps2esdi_gendisk.nr_real = ps2esdi_drives; + + for (i = 0; i < ps2esdi_drives; i++) { + ps2esdi[i << 6].nr_sects = + ps2esdi_info[i].head * + ps2esdi_info[i].sect * + ps2esdi_info[i].cyl; + ps2esdi_valid[i] = 1; + } + for (i = 0; i < (MAX_HD << 6); i++) { + ps2esdi_blocksizes[i] = 1024; + } + blksize_size[MAJOR_NR] = ps2esdi_blocksizes; +} /* ps2esdi_geninit */ + + +static void ps2esdi_get_device_cfg(void) +{ + u_short cmd_blk[TYPE_0_CMD_BLK_LENGTH]; + int drive; + + current_int_handler = ps2esdi_geometry_int_handler; + + for( drive = 0; drive < ps2esdi_drives; drive ++ ) { + printk( "%s: Drive %d\n", DEVICE_NAME, drive ); + cmd_blk[0] = CMD_GET_DEV_CONFIG | (drive << 5) | 0x600; + cmd_blk[1] = 0; + no_int_yet = TRUE; + ps2esdi_out_cmd_blk( cmd_blk ); + if( no_int_yet ) { + sleep_on( &ps2esdi_int ); + } + } +} + +/* strategy routine that handles most of the IO requests */ +static void do_ps2esdi_request(void) +{ + u_int block, count; + /* since, this routine is called with interrupts cleared - they + must be before it finishes */ + sti(); + +#if 0 + printk("%s:got request. device : %d minor : %d command : %d\n" + " sector : %ld count : %ld, buffer: %p\n", + DEVICE_NAME, + CURRENT_DEV, MINOR(CURRENT->rq_dev), + CURRENT->cmd, CURRENT->sector, + CURRENT->nr_sectors, CURRENT->buffer); +#endif + + /* standard macro that ensures that requests are really on the + list + sanity checks. */ + INIT_REQUEST; + + if ((u_int) CURRENT->buffer + CURRENT->nr_sectors * 512 > 16 * MB) { + printk("%s: DMA above 16MB not supported\n", DEVICE_NAME); + end_request(FAIL); + if (CURRENT) + do_ps2esdi_request(); + return; + } /* check for above 16Mb dmas */ + if ((CURRENT_DEV < ps2esdi_drives) && + (CURRENT->sector + CURRENT->nr_sectors <= + ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects)) { +#if 0 + printk("%s:got request. device : %d minor : %d command : %d \n" + " sector : %ld count : %ld\n", + DEVICE_NAME, + CURRENT_DEV, MINOR(CURRENT->rq_dev), + CURRENT->cmd, CURRENT->sector, + CURRENT->nr_sectors); +#endif + + + block = CURRENT->sector + ps2esdi[MINOR(CURRENT->rq_dev)].start_sect; + +#if 0 + printk("%s: blocknumber : %d\n", DEVICE_NAME, block); +#endif + count = CURRENT->nr_sectors; + switch (CURRENT->cmd) { + case READ: + ps2esdi_readwrite(READ, CURRENT_DEV, block, count); + return; + break; + case WRITE: + ps2esdi_readwrite(WRITE, CURRENT_DEV, block, count); + return; + break; + default: + printk("%s: Unknown command\n", DEVICE_NAME); + end_request(FAIL); + if (CURRENT) + do_ps2esdi_request(); + break; + } /* handle different commands */ + } else { + /* is request is valid */ + printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives, + CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects); + end_request(FAIL); + if (CURRENT) { + do_ps2esdi_request(); + } + } + +} /* main strategy routine */ + +/* resets the ESDI adapter */ +static void reset_ctrl(void) +{ + + u_long expire; + u_short status; + + /* enable interrupts on the controller */ + status = inb(ESDI_INTRPT); + outb((status & 0xe0) | ATT_EOI, ESDI_ATTN); /* to be sure we don't have + any interrupt pending... */ + outb_p(CTRL_ENABLE_INTR, ESDI_CONTROL); + + /* read the ESDI status port - if the controller is not busy, + simply do a soft reset (fast) - otherwise we'll have to do a + hard (slow) reset. */ + if (!(inb_p(ESDI_STATUS) & STATUS_BUSY)) { + /* soft reset */ + /*BA */ printk("%s: soft reset...\n", DEVICE_NAME); + outb_p(CTRL_SOFT_RESET, ESDI_ATTN); + } else { + /* hard reset */ + /*BA */ + printk("%s: hard reset...\n", DEVICE_NAME); + outb_p(CTRL_HARD_RESET, ESDI_CONTROL); + expire = jiffies + 200; + while (jiffies < expire); + outb_p(1, ESDI_CONTROL); + } +} + +/* called by the strategy routine to handle read and write requests */ +static void ps2esdi_readwrite(int cmd, u_char drive, u_int block, u_int count) +{ + + u_short track, head, cylinder, sector; + u_short cmd_blk[TYPE_1_CMD_BLK_LENGTH]; + + /* do some relevant arithmatic */ + CURRENT->current_nr_sectors = + (count < (2 * MAX_16BIT / SECT_SIZE)) ? count : (2 * MAX_16BIT / SECT_SIZE); + track = block / ps2esdi_info[drive].sect; + head = track % ps2esdi_info[drive].head; + cylinder = track / ps2esdi_info[drive].head; + sector = block % ps2esdi_info[drive].sect; + +#if 0 + printk("%s: cyl=%d head=%d sect=%d\n", DEVICE_NAME, cylinder, head, sector); +#endif + /* call the routine that actually fills out a command block */ + ps2esdi_fill_cmd_block + (cmd_blk, + (cmd == READ) ? CMD_READ : CMD_WRITE, + cylinder, head, sector, + CURRENT->current_nr_sectors, drive); + + /* send the command block to the controller */ + if (ps2esdi_out_cmd_blk(cmd_blk)) { + printk("%s: Controller failed\n", DEVICE_NAME); + if ((++CURRENT->errors) < MAX_RETRIES) { + return do_ps2esdi_request(); + } else { + end_request(FAIL); + if (CURRENT) { + do_ps2esdi_request(); + } + } + } else { + /* check for failure to put out the command block */ +#if 0 + printk("%s: waiting for xfer\n", DEVICE_NAME); +#endif + /* turn disk lights on */ + LITE_ON; + } + +} /* ps2esdi_readwrite */ + +/* fill out the command block */ +static void ps2esdi_fill_cmd_block(u_short * cmd_blk, u_short cmd, + u_short cyl, u_short head, u_short sector, u_short length, u_char drive) +{ + + cmd_blk[0] = (drive << 5) | cmd; + cmd_blk[1] = length; + cmd_blk[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; + cmd_blk[3] = (cyl & 0x3E0) >> 5; + +} /* fill out the command block */ + +/* write a command block to the controller */ +static int ps2esdi_out_cmd_blk(u_short * cmd_blk) +{ + + int i, j; + u_char status; + + /* enable interrupts */ + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + + /* do not write to the controller, if it is busy */ + for( i = jiffies + ESDI_STAT_TIMEOUT; + (i > jiffies) && (inb(ESDI_STATUS) & STATUS_BUSY); ); + + +#if 0 + printk("%s: i(1)=%d\n", DEVICE_NAME, i); +#endif + + /* if device is still busy - then just time out */ + if (inb(ESDI_STATUS) & STATUS_BUSY) { + printk("%s: ps2esdi_out_cmd timed out (1)\n", DEVICE_NAME); + return ERROR; + } /* timeout ??? */ + /* Set up the attention register in the controller */ + outb(((*cmd_blk) & 0xE0) | 1, ESDI_ATTN); + +#if 0 + printk("%s: sending %d words to controller\n", DEVICE_NAME, (((*cmd_blk) >> 14) + 1) << 1); +#endif + + /* one by one send each word out */ + for (i = (((*cmd_blk) >> 14) + 1) << 1; i; i--) { + status = inb(ESDI_STATUS); + for (j = jiffies + ESDI_STAT_TIMEOUT; + (j > jiffies) && (status & STATUS_BUSY) && + (status & STATUS_CMD_INF); status = inb(ESDI_STATUS)); + if ((status & (STATUS_BUSY | STATUS_CMD_INF)) == STATUS_BUSY) { +#if 0 + printk("%s: sending %04X\n", DEVICE_NAME, *cmd_blk); +#endif + outw(*cmd_blk++, ESDI_CMD_INT); + } else { + printk("%s: ps2esdi_out_cmd timed out while sending command (status=%02X)\n", + DEVICE_NAME, status); + return ERROR; + } + } /* send all words out */ + return OK; +} /* send out the commands */ + + +/* prepare for dma - do all the necessary setup */ +static void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode) +{ + u_int tc; + +#if 0 + printk("ps2esdi: b_wait: %p\n", CURRENT->bh->b_wait); +#endif + cli(); + + outb(dma_arb_level | DMA_MASK_CHAN, PORT_DMA_FN); + + outb(dma_arb_level | DMA_WRITE_ADDR, PORT_DMA_FN); + outb((u_int) buffer & (u_int) 0xff, PORT_DMA_EX); + outb(((u_int) buffer >> 8) & (u_int) 0xff, PORT_DMA_EX); + outb(((u_int) buffer >> 16) & (u_int) 0xff, PORT_DMA_EX); + + outb(dma_arb_level | DMA_WRITE_TC, PORT_DMA_FN); + tc = (length * SECT_SIZE / 2) - 1; + outb(tc & 0xff, PORT_DMA_EX); + outb((tc >> 8) & 0xff, PORT_DMA_EX); + + outb(dma_arb_level | DMA_WRITE_MODE, PORT_DMA_FN); + outb(dma_xmode, PORT_DMA_EX); + + outb(dma_arb_level | DMA_UNMASK_CHAN, PORT_DMA_FN); + + sti(); + +} /* prepare for dma */ + + + +static void ps2esdi_interrupt_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + u_int int_ret_code; + + if (inb(ESDI_STATUS) & STATUS_INTR) { + int_ret_code = inb(ESDI_INTRPT); + if (current_int_handler) { + /* Disable adapter interrupts till processing is finished */ + outb(CTRL_DISABLE_INTR, ESDI_CONTROL); + current_int_handler(int_ret_code); + } else { + printk("%s: help ! No interrupt handler.\n", DEVICE_NAME); + } + } +} + +static void ps2esdi_initial_reset_int_handler(u_int int_ret_code) +{ + + switch (int_ret_code & 0xf) { + case INT_RESET: + /*BA */ + printk("%s: initial reset completed.\n", DEVICE_NAME); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + wake_up(&ps2esdi_int); + break; + case INT_ATTN_ERROR: + printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME, + int_ret_code); + printk("%s: status: %02x\n", DEVICE_NAME, inb(ESDI_STATUS)); + break; + default: + printk("%s: initial reset handler received interrupt: %02X\n", + DEVICE_NAME, int_ret_code); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + break; + } + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); +} + + +static void ps2esdi_geometry_int_handler(u_int int_ret_code) +{ + u_int status, drive_num; + unsigned long rba; + int i; + + drive_num = int_ret_code >> 5; + switch (int_ret_code & 0xf) { + case INT_CMD_COMPLETE: + for (i = ESDI_TIMEOUT; i & !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--); + if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { + printk("%s: timeout reading status word\n", DEVICE_NAME); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + break; + } + status = inw(ESDI_STT_INT); + if ((status & 0x1F) == CMD_GET_DEV_CONFIG) { +#define REPLY_WORDS 5 /* we already read word 0 */ + u_short reply[REPLY_WORDS]; + + if (ps2esdi_read_status_words((status >> 8) - 1, REPLY_WORDS, reply)) { + /*BA */ + printk("%s: Device Configuration Status for drive %u\n", + DEVICE_NAME, drive_num); + + printk( "%s: Spares/cyls: %u\n", DEVICE_NAME, reply[0] >> 8); + + printk( "%s: Config bits: %s%s%s%s%s\n", DEVICE_NAME, + (reply[0] & CONFIG_IS) ? "Invalid Secondary, " : "", + ((reply[0] & CONFIG_ZD) && !(reply[0] & CONFIG_IS)) + ? "Zero Defect, " : "Defects Present, ", + (reply[0] & CONFIG_SF) ? "Skewed Format, " : "", + (reply[0] & CONFIG_FR) ? "Removable, " : "Non-Removable, ", + (reply[0] & CONFIG_RT) ? "No Retries" : "Retries" ); + + rba = reply[1] | ((unsigned long) reply[2] << 16); + printk("%s: Number of RBA's: %lu\n", DEVICE_NAME, rba); + + if (!ps2esdi_info[drive_num].head) { + ps2esdi_info[drive_num].ctl = 8; + if (tp720esdi) { /* store the retrieved parameters */ + ps2esdi_info[0].head = reply[4] & 0xff; + ps2esdi_info[0].sect = reply[4] >> 8; + ps2esdi_info[0].cyl = reply[3]; + ps2esdi_info[0].wpcom = 0; + ps2esdi_info[0].lzone = reply[3]; + } else { + ps2esdi_info[drive_num].head = 64; + ps2esdi_info[drive_num].sect = 32; + ps2esdi_info[drive_num].cyl = rba / (64 * 32); + ps2esdi_info[drive_num].wpcom = 0; + ps2esdi_info[drive_num].lzone = ps2esdi_info[drive_num].cyl; + } + + if( !integrated_esdi ) { + ps2esdi_drives++; + } + } + printk("%s: Physical number of cylinders: %u, " + "Sectors/Track: %u, Heads: %u\n", + DEVICE_NAME, ps2esdi_info[drive_num].cyl, + ps2esdi_info[drive_num].sect, + ps2esdi_info[drive_num].head ); + + } else + printk("%s: failed while getting device config\n", DEVICE_NAME); +#undef REPLY_WORDS + } else { + printk("%s: command %02X unknown by geometry handler\n", + DEVICE_NAME, status & 0x1f); + } + + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + break; + + case INT_ATTN_ERROR: + printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME, + int_ret_code); + printk("%s: Device not available\n", DEVICE_NAME); + ps2esdi_info[drive_num].head = 0; + break; + case INT_CMD_ECC: + case INT_CMD_RETRY: + case INT_CMD_ECC_RETRY: + case INT_CMD_WARNING: + case INT_CMD_ABORT: + case INT_CMD_FAILED: + case INT_DMA_ERR: + case INT_CMD_BLK_ERR: + /*BA */ + printk("%s :Whaa. Error occurred...Assuming drive %d not there\n", + DEVICE_NAME, drive_num ); + ps2esdi_info[drive_num].head = 0; +#if 0 + dump_cmd_complete_status(int_ret_code); +#endif + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + break; + default: + printk("%s: Unknown interrupt reason: %02X\n", + DEVICE_NAME, int_ret_code & 0xf); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + break; + } + + wake_up(&ps2esdi_int); + no_int_yet = FALSE; + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); +} + +static void ps2esdi_normal_interrupt_handler(u_int int_ret_code) +{ + u_int status; + int i; + + switch (int_ret_code & 0x0f) { + case INT_TRANSFER_REQ: + ps2esdi_prep_dma(CURRENT->buffer, CURRENT->current_nr_sectors, + (CURRENT->cmd == READ) ? DMA_READ_16 : DMA_WRITE_16); + outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + + case INT_ATTN_ERROR: + printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME, + int_ret_code); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + + case INT_CMD_COMPLETE: + for (i = ESDI_TIMEOUT; i & !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--); + if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { + printk("%s: timeout reading status word\n", DEVICE_NAME); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + if ((++CURRENT->errors) < MAX_RETRIES) + do_ps2esdi_request(); + else { + end_request(FAIL); + if (CURRENT) + do_ps2esdi_request(); + } + break; + } + status = inw(ESDI_STT_INT); + switch (status & 0x1F) { + case (CMD_READ & 0xff): + case (CMD_WRITE & 0xff): + LITE_OFF; + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); +#if 0 + printk("ps2esdi: cmd_complete b_wait: %p\n", CURRENT->bh->b_wait); +#endif + ps2esdi_continue_request(); + break; + default: + printk("%s: interrupt for unknown command %02X\n", + DEVICE_NAME, status & 0x1f); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + } + break; + case INT_CMD_ECC: + case INT_CMD_RETRY: + case INT_CMD_ECC_RETRY: + LITE_OFF; + dump_cmd_complete_status(int_ret_code); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + ps2esdi_continue_request(); + break; + case INT_CMD_WARNING: + case INT_CMD_ABORT: + case INT_CMD_FAILED: + case INT_DMA_ERR: + LITE_OFF; + printk("%s: command error\n", DEVICE_NAME ); + dump_cmd_complete_status(int_ret_code); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + if ((++CURRENT->errors) < MAX_RETRIES) + do_ps2esdi_request(); + else { + end_request(FAIL); + if (CURRENT) + do_ps2esdi_request(); + } + break; + + case INT_CMD_BLK_ERR: + printk("%s: block error\n", DEVICE_NAME ); + dump_cmd_complete_status(int_ret_code); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + end_request(FAIL); + if (CURRENT) + do_ps2esdi_request(); + break; + + case INT_CMD_FORMAT: + printk("%s: huh ? Who issued this format command ?\n" + ,DEVICE_NAME); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + + case INT_RESET: + /* BA printk("%s: reset completed.\n", DEVICE_NAME) */ ; + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + + default: + printk("%s: Unknown interrupt reason: %02X\n", + DEVICE_NAME, int_ret_code & 0xf); + outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + break; + } + +} /* handle interrupts */ + + +static void ps2esdi_continue_request(void) +{ + if (CURRENT->nr_sectors -= CURRENT->current_nr_sectors) { + CURRENT->buffer += CURRENT->current_nr_sectors * SECT_SIZE; + CURRENT->sector += CURRENT->current_nr_sectors; + do_ps2esdi_request(); + } else { + end_request(SUCCES); + if (CURRENT) + do_ps2esdi_request(); + } +} + + + +static int ps2esdi_read_status_words(int num_words, + int max_words, + u_short * buffer) +{ + int i; + + for (; max_words && num_words; max_words--, num_words--, buffer++) { + for (i = ESDI_TIMEOUT; i && !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--); + if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { + printk("%s: timeout reading status word\n", DEVICE_NAME); + return FAIL; + } + *buffer = inw(ESDI_STT_INT); + } + return SUCCES; +} + + + + +static void dump_cmd_complete_status(u_int int_ret_code) +{ +#define WAIT_FOR_STATUS \ + for(i=ESDI_TIMEOUT;i && !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL);i--); \ + if(!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { \ + printk("%s: timeout reading status word\n",DEVICE_NAME); \ + return; \ + } + + int i, word_count; + u_short stat_word; + u_long rba; + + printk("%s: Status Dump:\n Device: %u, interrupt ID: %02X\n", + DEVICE_NAME, int_ret_code >> 5, + int_ret_code & 0xf); + + WAIT_FOR_STATUS; + stat_word = inw(ESDI_STT_INT); + word_count = (stat_word >> 8) - 1; + printk(" %u status words, command: %02X\n", word_count, + stat_word & 0xff); + + if( word_count ) { + word_count --; + WAIT_FOR_STATUS; + stat_word = inw(ESDI_STT_INT); + printk(" command status code: %02X, command error code: %02X\n", + stat_word >> 8, stat_word & 0xff); + } + if (word_count) { + word_count --; + WAIT_FOR_STATUS; + stat_word = inw(ESDI_STT_INT); + printk(" device error code: %s%s%s%s%s,%02X\n", + (stat_word & 0x1000) ? "Ready, " : "Not Ready, ", + (stat_word & 0x0800) ? "Selected, " : "Not Selected, ", + (stat_word & 0x0400) ? "Write Fault, " : "", + (stat_word & 0x0200) ? "Track 0, " : "", + (stat_word & 0x0100) ? "Seek or command complete, " : "", + stat_word >> 8); + } + if (word_count) { + word_count --; + WAIT_FOR_STATUS; + stat_word = inw(ESDI_STT_INT); + printk(" Blocks to do: %u", stat_word); + } + if (word_count) { + word_count -= 2; + WAIT_FOR_STATUS; + rba = inw(ESDI_STT_INT); + WAIT_FOR_STATUS; + rba |= inw(ESDI_STT_INT) << 16; + printk(", Last Cyl: %u Head: %u Sector: %u\n", + (u_short) ((rba & 0x1ff80000) >> 11), + (u_short) ((rba & 0x7E0) >> 5), (u_short) (rba & 0x1f)); + } else { + printk("\n"); + } + + if (word_count) { + word_count --; + WAIT_FOR_STATUS; + stat_word = inw(ESDI_STT_INT); + printk(" Blocks required ECC: %u", stat_word); + } + printk("\n"); + +#undef WAIT_FOR_STATUS + +} + + + +static int ps2esdi_open(struct inode *inode, struct file *file) +{ + int dev = DEVICE_NR(MINOR(inode->i_rdev)); + +#if 0 + printk("%s: dev= %d\n", DEVICE_NAME, dev); +#endif + + if (dev < ps2esdi_drives) { + while (!ps2esdi_valid[dev]) + sleep_on(&ps2esdi_wait_open); + + access_count[dev]++; + + return (0); + } else + return (-ENODEV); +} + + + +static void ps2esdi_release(struct inode *inode, struct file *file) +{ + int dev = DEVICE_NR(MINOR(inode->i_rdev)); + + if (dev < ps2esdi_drives) { + sync_dev(dev); + access_count[dev]--; + } +} + + + +static int ps2esdi_ioctl(struct inode *inode, + struct file *file, u_int cmd, u_long arg) +{ + + struct ps2esdi_geometry *geometry = (struct ps2esdi_geometry *) arg; + int dev = DEVICE_NR(MINOR(inode->i_rdev)), err; + + if (inode && (dev < ps2esdi_drives)) + switch (cmd) { + case HDIO_GETGEO: + if (arg) { + if ((err = verify_area(VERIFY_WRITE, geometry, sizeof(*geometry)))) + return (err); + put_fs_byte(ps2esdi_info[dev].head, (char *) &geometry->heads); + put_fs_byte(ps2esdi_info[dev].sect, (char *) &geometry->sectors); + put_fs_word(ps2esdi_info[dev].cyl, (short *) &geometry->cylinders); + put_fs_long(ps2esdi[MINOR(inode->i_rdev)].start_sect, + (long *) &geometry->start); + + return (0); + } + break; + case BLKRASET: + if (!suser()) + return -EACCES; + if (!inode->i_rdev) + return -EINVAL; + if (arg > 0xff) + return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKGETSIZE: + if (arg) { + if ((err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)))) + return (err); + put_fs_long(ps2esdi[MINOR(inode->i_rdev)].nr_sects, (long *) arg); + + return (0); + } + break; + case BLKFLSBUF: + if (!suser()) + return -EACCES; + if (!inode->i_rdev) + return -EINVAL; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + + case BLKRRPART: + return (ps2esdi_reread_partitions(inode->i_rdev)); + RO_IOCTLS(inode->i_rdev, arg); + } + return (-EINVAL); +} + + + +static int ps2esdi_reread_partitions(int dev) +{ + int target = DEVICE_NR(MINOR(dev)); + int start = target << ps2esdi_gendisk.minor_shift; + int partition; + + cli(); + ps2esdi_valid[target] = (access_count[target] != 1); + sti(); + if (ps2esdi_valid[target]) + return (-EBUSY); + + for (partition = ps2esdi_gendisk.max_p - 1; + partition >= 0; partition--) { + sync_dev(MAJOR_NR << 8 | start | partition); + invalidate_inodes(MAJOR_NR << 8 | start | partition); + invalidate_buffers(MAJOR_NR << 8 | start | partition); + ps2esdi_gendisk.part[start + partition].start_sect = 0; + ps2esdi_gendisk.part[start + partition].nr_sects = 0; + }; + + ps2esdi_gendisk.part[start].nr_sects = ps2esdi_info[target].head * + ps2esdi_info[target].cyl * ps2esdi_info[target].sect; + resetup_one_dev(&ps2esdi_gendisk, target); + + ps2esdi_valid[target] = 1; + wake_up(&ps2esdi_wait_open); + + return (0); +} + +void ps2esdi_reset_timer(unsigned long unused) +{ + + int status; + + status = inb(ESDI_INTRPT); + if ((status & 0xf) == INT_RESET) { + outb((status & 0xe0) | ATT_EOI, ESDI_ATTN); + outb(CTRL_ENABLE_INTR, ESDI_CONTROL); + reset_status = 1; + } + wake_up(&ps2esdi_int); +} + +#endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/char/serial.c linux-2.0.35-mca/drivers/char/serial.c --- linux/drivers/char/serial.c Sat Aug 8 21:30:10 1998 +++ linux-2.0.35-mca/drivers/char/serial.c Sat Aug 8 22:20:47 1998 @@ -202,6 +202,14 @@ { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ #endif +#ifdef CONFIG_MCA + { 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS }, + { 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS }, + { 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS }, + { 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS }, + { 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS }, + { 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS }, +#endif }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct)) diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/3c509.c linux-2.0.35-mca/drivers/net/3c509.c --- linux/drivers/net/3c509.c Sat Aug 8 21:30:12 1998 +++ linux-2.0.35-mca/drivers/net/3c509.c Sat Aug 8 22:20:47 1998 @@ -1,8 +1,8 @@ /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ /* - Written 1993-1997 by Donald Becker. + Written 1993-1998 by Donald Becker. - Copyright 1994-1997 by Donald Becker. + Copyright 1994-1998 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, @@ -33,24 +33,26 @@ delays v1.10 4/21/97 Fixed module code so that multiple cards may be detected, other cleanups. -djb + v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb + v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb + v1.15 1/31/98 Faster recovery for Tx errors. -djb + v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb */ -static char *version = "3c509.c:1.12 6/4/97 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = "3c509.c:1.16 2/3/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (400*HZ/1000) /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -#define INTR_WORK 10 +static int max_interrupt_work = 10; #include #include -#include #include #include #include -#include #include #include #include @@ -58,7 +60,7 @@ #include #include #include -#include /* for CONFIG_MCA */ +#include #include /* for udelay() */ #include @@ -128,15 +130,15 @@ int head, size; struct sk_buff *queue[SKB_QUEUE_SIZE]; }; -static int id_port = 0x100; +static int id_port = 0x110; /* Start with 0x110 to avoid new sound cards.*/ static struct device *el3_root_dev = NULL; static ushort id_read_eeprom(int index); -static ushort read_eeprom(short ioaddr, int index); +static ushort read_eeprom(int ioaddr, int index); static int el3_open(struct device *dev); static int el3_start_xmit(struct sk_buff *skb, struct device *dev); static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void update_stats(int addr, struct device *dev); +static void update_stats(struct device *dev); static struct enet_statistics *el3_get_stats(struct device *dev); static int el3_rx(struct device *dev); static int el3_close(struct device *dev); @@ -144,11 +146,27 @@ +#ifdef CONFIG_MCA +struct el3_mca_adapters_struct { + char* name; + int id; +}; + +struct el3_mca_adapters_struct el3_mca_adapters[] = { + { "3Com 3c529 EtherLink III (10base2)", 0x627c }, + { "3Com 3c529 EtherLink III (10baseT)", 0x627d }, + { "3Com 3c529 EtherLink III (test mode)", 0x62db }, + { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 }, + { "3Com 3c529 EtherLink III (TP)", 0x62f7 }, + { NULL, 0 }, +}; +#endif + int el3_probe(struct device *dev) { short lrs_state = 0xff, i; - ushort ioaddr, irq, if_port; - short phys_addr[3]; + int ioaddr, irq, if_port; + u16 phys_addr[3]; static int current_tag = 0; /* First check all slots of the EISA bus. The next slot address to @@ -180,21 +198,60 @@ } #ifdef CONFIG_MCA - if (MCA_bus) { - mca_adaptor_select_mode(1); - for (i = 0; i < 8; i++) - if ((mca_adaptor_id(i) | 1) == 0x627c) { - ioaddr = mca_pos_base_addr(i); - irq = inw(ioaddr + WN0_IRQ) >> 12; - if_port = inw(ioaddr + 6)>>14; - for (i = 0; i < 3; i++) + /* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch, heavily + modified by Chris Beauregard (cpbeaure@csclub.uwaterloo.ca) + to support standard MCA probing. */ + + /* redone for multi-card detection by ZP Gu (zpg@castle.net) */ + /* now works as a module */ + + if( MCA_bus ) { + int slot, j; + u_char pos4, pos5; + + for( j = 0; el3_mca_adapters[j].name != NULL; j ++ ) { + slot = 0; + while( slot != MCA_NOTFOUND ) { + slot = mca_find_unused_adapter( el3_mca_adapters[j].id, slot ); + if( slot == MCA_NOTFOUND ) break; + + /* if we get this far, an adapter has been detected and is + enabled */ + + printk("3c509: found %s at slot %d\n", + el3_mca_adapters[j].name, slot + 1 ); + + pos4 = mca_read_stored_pos( slot, 4 ); + pos5 = mca_read_stored_pos( slot, 5 ); + + ioaddr = ((short)((pos4&0xfc)|0x02)) << 8; + irq = pos5 & 0x0f; + + /* probing for a card at a particular IO/IRQ */ + if (dev && + ((dev->irq && dev->irq != irq) || + (dev->base_addr && dev->base_addr != ioaddr))) { + slot++; /* probing next slot */ + continue; + } + + /* claim the slot */ + mca_set_adapter_name( slot, el3_mca_adapters[j].name ); + + if_port = pos4 & 0x03; + if (el3_debug > 2) { + printk("3c529: irq %d ioaddr 0x%x ifport %d\n", irq, ioaddr, + if_port); + } + for (i = 0; i < 3; i++) { phys_addr[i] = htons(read_eeprom(ioaddr, i)); - - mca_adaptor_select_mode(0); + } + goto found; } - mca_adaptor_select_mode(0); - + } + /* if we get here, we didn't find an MCA adapter */ + return -ENODEV; } #endif @@ -202,7 +259,7 @@ outb(0x02, 0x279); /* Select PnP config control register. */ outb(0x02, 0xA79); /* Return to WaitForKey state. */ /* Select an open I/O location at 0x1*0 to do contention select. */ - for (id_port = 0x100; id_port < 0x200; id_port += 0x10) { + for ( ; id_port < 0x200; id_port += 0x10) { if (check_region(id_port, 1)) continue; outb(0x00, id_port); @@ -246,18 +303,23 @@ } { - unsigned short iobase = id_read_eeprom(8); + unsigned int iobase = id_read_eeprom(8); if_port = iobase >> 14; ioaddr = 0x200 + ((iobase & 0x1f) << 4); } - if (dev && dev->irq > 1 && dev->irq < 16) - irq = dev->irq; - else - irq = id_read_eeprom(9) >> 12; + irq = id_read_eeprom(9) >> 12; - if (dev && dev->base_addr != 0 - && dev->base_addr != (unsigned short)ioaddr) { - return -ENODEV; + if (dev) { /* Set passed-in IRQ or I/O Addr. */ + if (dev->irq > 1 && dev->irq < 16) + irq = dev->irq; + + if (dev->base_addr) { + if (dev->mem_end == 0x3c509 /* Magic key */ + && dev->base_addr >= 0x200 && dev->base_addr <= 0x3e0) + ioaddr = dev->base_addr & 0x3f0; + else if (dev->base_addr != ioaddr) + return -ENODEV; + } } /* Set the adaptor tag so that the next card can be found. */ @@ -322,7 +384,7 @@ /* Read a word from the EEPROM using the regular EEPROM access register. Assume that we are in register window zero. */ -static ushort read_eeprom(short ioaddr, int index) +static ushort read_eeprom(int ioaddr, int index) { outw(EEPROM_READ + index, ioaddr + 10); /* Pause for at least 162 us. for the read to take place. */ @@ -419,7 +481,7 @@ /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull, + outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, ioaddr + EL3_CMD); if (el3_debug > 3) @@ -521,7 +583,7 @@ { struct device *dev = (struct device *)dev_id; int ioaddr, status; - int i = INTR_WORK; + int i = max_interrupt_work; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); @@ -533,10 +595,11 @@ dev->interrupt = 1; ioaddr = dev->base_addr; - status = inw(ioaddr + EL3_STATUS); - if (el3_debug > 4) + if (el3_debug > 4) { + status = inw(ioaddr + EL3_STATUS); printk("%s: interrupt, status %4.4x.\n", dev->name, status); + } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete | StatsFull)) { @@ -552,14 +615,26 @@ dev->tbusy = 0; mark_bh(NET_BH); } - if (status & (AdapterFailure | RxEarly | StatsFull)) { + if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { /* Handle all uncommon interrupts. */ if (status & StatsFull) /* Empty statistics. */ - update_stats(ioaddr, dev); + update_stats(dev); if (status & RxEarly) { /* Rx early is unused. */ el3_rx(dev); outw(AckIntr | RxEarly, ioaddr + EL3_CMD); } + if (status & TxComplete) { /* Really Tx error. */ + struct el3_private *lp = (struct el3_private *)dev->priv; + short tx_status; + int i = 4; + + while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { + if (tx_status & 0x38) lp->stats.tx_aborted_errors++; + if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); + if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); + outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ + } + } if (status & AdapterFailure) { /* Adapter failure requires Rx reset and reinit. */ outw(RxReset, ioaddr + EL3_CMD); @@ -602,7 +677,7 @@ save_flags(flags); cli(); - update_stats(dev->base_addr, dev); + update_stats(dev); restore_flags(flags); return &lp->stats; } @@ -612,9 +687,10 @@ operation, and it's simpler for the rest of the driver to assume that window 1 is always valid rather than use a special window-state variable. */ -static void update_stats(int ioaddr, struct device *dev) +static void update_stats(struct device *dev) { struct el3_private *lp = (struct el3_private *)dev->priv; + int ioaddr = dev->base_addr; if (el3_debug > 5) printk(" Updating the statistics.\n"); @@ -653,6 +729,8 @@ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ short error = rx_status & 0x3800; + + outw(RxDiscard, ioaddr + EL3_CMD); lp->stats.rx_errors++; switch (error) { case 0x0000: lp->stats.rx_over_errors++; break; @@ -683,17 +761,19 @@ (pkt_len + 3) >> 2); #endif + outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ lp->stats.rx_packets++; continue; - } else if (el3_debug) + } + outw(RxDiscard, ioaddr + EL3_CMD); + lp->stats.rx_dropped++; + if (el3_debug) printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } - lp->stats.rx_dropped++; - outw(RxDiscard, ioaddr + EL3_CMD); + inw(ioaddr + EL3_STATUS); /* Delay. */ while (inw(ioaddr + EL3_STATUS) & 0x1000) printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); @@ -708,7 +788,7 @@ static void set_multicast_list(struct device *dev) { - short ioaddr = dev->base_addr; + int ioaddr = dev->base_addr; if (el3_debug > 1) { static int old = 0; if (old != dev->mc_count) { @@ -760,13 +840,13 @@ /* But we explicitly zero the IRQ line select anyway. */ outw(0x0f00, ioaddr + WN0_IRQ); - update_stats(ioaddr, dev); + update_stats(dev); MOD_DEC_USE_COUNT; return 0; } #ifdef MODULE -/* Parameter that may be passed into the module. */ +/* Parameters that may be passed into the module. */ static int debug = -1; static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/3c523.c linux-2.0.35-mca/drivers/net/3c523.c --- linux/drivers/net/3c523.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/3c523.c Sat Aug 8 22:34:35 1998 @@ -0,0 +1,1341 @@ +/* +net-3-driver for the 3c523 Etherlink/MC card (i82586 Ethernet chip) + + +This is an extension to the Linux operating system, and is covered by the +same Gnu Public License that covers that work. + +Copyright 1995, 1996 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca) + +This is basically Michael Hipp's ni52 driver, with a new probing +algorithm and some minor changes to the 82586 CA and reset routines. +Thanks a lot Michael for a really clean i82586 implementation! Unless +otherwise documented in ni52.c, any bugs are mine. + +Contrary to the Ethernet-HOWTO, this isn't based on the 3c507 driver in +any way. The ni52 is a lot easier to modify. + +sources: + ni52.c + + Crynwr packet driver collection was a great reference for my first + attempt at this sucker. The 3c507 driver also helped, until I noticed + that ni52.c was a lot nicer. + + EtherLink/MC: Micro Channel Ethernet Adapter Technical Reference + Manual, courtesy of 3Com CardFacts, documents the 3c523-specific + stuff. Information on CardFacts is found in the Ethernet HOWTO. + Also see + + Microprocessor Communications Support Chips, T.J. Byers, ISBN + 0-444-01224-9, has a section on the i82586. It tells you just enough + to know that you really don't want to learn how to program the chip. + + The original device probe code was stolen from ps2esdi.c + +Known Problems: + Since most of the code was stolen from ni52.c, you'll run across the + same bugs in the 0.62 version of ni52.c, plus maybe a few because of + the 3c523 idiosynchacies. The 3c523 has 16K of RAM though, so there + shouldn't be the overrun problem that the 8K ni52 has. + + This driver is for a 16K adapter. It should work fine on the 64K + adapters, but it will only use one of the 4 banks of RAM. Modifying + this for the 64K version would require a lot of heinous bank + switching, which I'm sure not interested in doing. If you try to + implement a bank switching version, you'll basically have to remember + what bank is enabled and do a switch everytime you access a memory + location that's not current. You'll also have to remap pointers on + the driver side, because it only knows about 16K of the memory. + Anyone desperate or masochistic enough to try? + + It seems to be stable now when multiple transmit buffers are used. I + can't see any performance difference, but then I'm working on a 386SX. + + Multicast doesn't work. It doesn't even pretend to work. Don't use + it. Don't compile your kernel with multicast support. I don't know + why. + +Features: + This driver is useable as a loadable module. If you try to specify an + IRQ or a IO address (via insmod 3c523.o irq=xx io=0xyyy), it will + search the MCA slots until it finds a 3c523 with the specified + parameters. + + This driver should support multiple ethernet cards, but I can't test + that. If someone would I'd greatly appreciate it. + + This has been tested with both BNC and TP versions, internal and + external transceivers. Haven't tested with the 64K version (that I + know of). + +History: + Jan 1st, 1996 + first public release + Feb 4th, 1996 + update to 1.3.59, incorporated multicast diffs from ni52.c + Feb 15th, 1996 + added shared irq support + + $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#include +#include +#endif + +#include +#include +#include + +#include "3c523.h" + +/*************************************************************************/ +#define DEBUG /* debug on */ +#define SYSBUSVAL 0 /* 1 = 8 Bit, 0 = 16 bit - 3c523 only does 16 bit*/ + +#define make32(ptr16) (p->memtop + (short) (ptr16) ) +#define make24(ptr32) ((char *) (ptr32) - p->base) +#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop )) + +/*************************************************************************/ +/* + Tables to which we can map values in the configuration registers. +*/ +static int irq_table[] = {12, 7, 3, 9}; +static int csr_table[] = {0x300, 0x1300, 0x2300, 0x3300}; +static int shm_table[] = {0x0c0000, 0x0c8000, 0x0d0000, 0x0d8000}; + +/******************* how to calculate the buffers ***************************** + + + * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works + * --------------- in a different (more stable?) mode. Only in this mode it's + * possible to configure the driver with 'NO_NOPCOMMANDS' + +sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8; +sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT +sizeof(rfd) = 24; sizeof(rbd) = 12; +sizeof(tbd) = 8; sizeof(transmit_cmd) = 16; +sizeof(nop_cmd) = 8; + + * if you don't know the driver, better do not change this values: */ + +#define RECV_BUFF_SIZE 1524 /* slightly oversized */ +#define XMIT_BUFF_SIZE 1524 /* slightly oversized */ +#define NUM_XMIT_BUFFS 4 /* config for both, 8K and 16K shmem */ +#define NUM_RECV_BUFFS_8 1 /* config for 8K shared mem */ +#define NUM_RECV_BUFFS_16 6 /* config for 16K shared mem */ + +#if (NUM_XMIT_BUFFS == 1) +#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */ +#endif + +/**************************************************************************/ + +#define DELAY(x) {int i=jiffies; \ + if(loops_per_sec == 1) \ + while(i+(x)>jiffies); \ + else \ + __delay((loops_per_sec>>5)*x); \ + } + +/* a much shorter delay: */ +#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); } + +/* wait for command with timeout: */ +#define WAIT_4_SCB_CMD() { int i; \ + for(i=0;i<1024;i++) { \ + if(!p->scb->cmd) break; \ + DELAY_16(); \ + if(i == 1023) { \ + printk("%s:%d: scb_cmd timed out .. resetting i82586\n",\ + dev->name,__LINE__); \ + elmc_id_reset586(); } } } + +static void elmc_interrupt(int irq,void *dev_id,struct pt_regs *reg_ptr); +static int elmc_open(struct device *dev); +static int elmc_close(struct device *dev); +static int elmc_send_packet(struct sk_buff *,struct device *); +static struct enet_statistics *elmc_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +/* helper-functions */ +static int init586(struct device *dev); +static int check586(struct device *dev,char *where,unsigned size); +static void alloc586(struct device *dev); +static void startrecv586(struct device *dev); +static void *alloc_rfa(struct device *dev,void *ptr); +static void elmc_rcv_int(struct device *dev); +static void elmc_xmt_int(struct device *dev); +static void elmc_rnr_int(struct device *dev); + +struct priv { + struct enet_statistics stats; + unsigned long base; + char *memtop; + volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first; + volatile struct scp_struct *scp; /* volatile is important */ + volatile struct iscp_struct *iscp; /* volatile is important */ + volatile struct scb_struct *scb; /* volatile is important */ + volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS]; + volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS]; +#if (NUM_XMIT_BUFFS == 1) + volatile struct nop_cmd_struct *nop_cmds[2]; +#else + volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS]; +#endif + volatile int nop_point,num_recv_buffs; + volatile char *xmit_cbuffs[NUM_XMIT_BUFFS]; + volatile int xmit_count,xmit_last; + volatile int slot; +}; + +#define elmc_attn586() {elmc_do_attn586(dev->base_addr,ELMC_CTRL_INTE);} +#define elmc_reset586() {elmc_do_reset586(dev->base_addr,ELMC_CTRL_INTE);} + +/* with interrupts disabled - this will clear the interrupt bit in the +3c523 control register, and won't put it back. This effectively +disables interrupts on the card. */ +#define elmc_id_attn586() {elmc_do_attn586(dev->base_addr,0);} +#define elmc_id_reset586() {elmc_do_reset586(dev->base_addr,0);} + +/*************************************************************************/ +/* Do a Channel Attention on the 3c523. This is extremely board dependent. */ +static +void +elmc_do_attn586( int ioaddr, int ints ) { + /* the 3c523 requires a minimum of 500 ns. The delays here might be + a little too large, and hence they may cut the performance of the + card slightly. If someone who knows a little more about Linux + timing would care to play with these, I'd appreciate it. */ + + /* this bit masking stuff is crap. I'd rather have separate + registers with strobe triggers for each of these functions. + Ya take what ya got. */ + + outb( ELMC_CTRL_RST|0x3|ELMC_CTRL_CA|ints, ioaddr + ELMC_CTRL); + DELAY_16(); /* > 500 ns */ + outb( ELMC_CTRL_RST|0x3|ints, ioaddr + ELMC_CTRL); +} + +/*************************************************************************/ +/* + Reset the 82586 on the 3c523. Also very board dependent. +*/ +static +void +elmc_do_reset586( int ioaddr, int ints ) { + /* toggle the RST bit low then high */ + outb( 0x3|ELMC_CTRL_LBK, ioaddr + ELMC_CTRL ); + DELAY_16(); /* > 500 ns */ + outb( ELMC_CTRL_RST|ELMC_CTRL_LBK|0x3, ioaddr + ELMC_CTRL ); + + elmc_do_attn586( ioaddr, ints ); +} + +/********************************************** + * close device + */ + +static +int +elmc_close(struct device *dev) { + elmc_id_reset586(); /* the hard way to stop the receiver */ + + free_irq(dev->irq, dev); + + dev->start = 0; + dev->tbusy = 0; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +/********************************************** + * open device + */ + +static +int +elmc_open(struct device *dev) { + + elmc_id_attn586(); /* disable interrupts */ + + if(request_irq( dev->irq, &elmc_interrupt, SA_SHIRQ|SA_SAMPLE_RANDOM, + "3c523", dev ) + ) { + printk( "%s: couldn't get irq %d\n", dev->name, dev->irq ); + elmc_id_reset586(); + return -EAGAIN; + } + + alloc586(dev); + init586(dev); + startrecv586(dev); + + dev->interrupt = 0; + dev->tbusy = 0; + dev->start = 1; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; /* most done by init */ +} + +/********************************************** + * Check to see if there's an 82586 out there. + */ + +static +int +check586( struct device *dev, char *where, unsigned size) { + struct priv *p = (struct priv *) dev->priv; + char *iscp_addrs[2]; + int i = 0; + + p->base = (unsigned long) where + size - 0x01000000; + p->memtop = where + size; + p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS); + memset((char *)p->scp,0, sizeof(struct scp_struct)); + p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */ + + iscp_addrs[0] = where; + iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct); + + for( i = 0; i < 2; i ++ ) { + p->iscp = (struct iscp_struct *) iscp_addrs[i]; + memset((char *)p->iscp,0, sizeof(struct iscp_struct)); + + p->scp->iscp = make24(p->iscp); + p->iscp->busy = 1; + + elmc_id_reset586(); + + /* reset586 does an implicit CA */ + + /* apparently, you sometimes have to kick the 82586 twice... */ + elmc_id_attn586(); + + if(p->iscp->busy) { /* i82586 clears 'busy' after successful init */ + return 0; + } + } + + return 1; +} + +/****************************************************************** + * set iscp at the right place, called by elmc_probe and open586. + */ + +void +alloc586( struct device *dev ) { + struct priv *p = (struct priv *) dev->priv; + + elmc_id_reset586(); + DELAY(2); + + p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS); + p->scb = (struct scb_struct *) (dev->mem_start); + p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct)); + + memset((char *) p->iscp,0,sizeof(struct iscp_struct)); + memset((char *) p->scp ,0,sizeof(struct scp_struct)); + + p->scp->iscp = make24(p->iscp); + p->scp->sysbus = SYSBUSVAL; + p->iscp->scb_offset = make16(p->scb); + + p->iscp->busy = 1; + elmc_id_reset586(); + elmc_id_attn586(); + + DELAY(2); + + if(p->iscp->busy) { + printk("%s: Init-Problems (alloc).\n",dev->name); + } + + memset((char *)p->scb,0,sizeof(struct scb_struct)); +} + +/*****************************************************************/ +static int +elmc_getinfo( char* buf, int slot, void* d ) { + int len = 0; + struct device* dev = (struct device*) d; + int i; + + if( dev == NULL ) return len; + + len += sprintf( buf+len, "Revision: 0x%x\n", + inb( dev->base_addr + ELMC_REVISION ) & 0xf ); + len += sprintf( buf+len, "IRQ: %d\n", dev->irq ); + len += sprintf( buf+len, "IO Address: %#lx-%#lx\n", dev->base_addr, + dev->base_addr+ELMC_IO_EXTENT ); + len += sprintf( buf+len, "Memory: %#lx-%#lx\n", dev->mem_start, + dev->mem_end-1 ); + len += sprintf( buf+len, "Transceiver: %s\n", dev->if_port ? + "External" : "Internal" ); + len += sprintf( buf+len, "Device: %s\n", dev->name ); + len += sprintf( buf+len, "Hardware Address:" ); + for (i = 0; i < 6; i ++ ) { + len += sprintf( buf+len, " %02x", dev->dev_addr[i] ); + } + buf[len++] = '\n'; + buf[len] = 0; + + return len; +} /* elmc_getinfo() */ + +/*****************************************************************/ +int +elmc_probe(struct device *dev) { + static int slot = 0; + int base_addr = dev ? dev->base_addr : 0; + int irq = dev ? dev->irq : 0; + u_char status = 0; + u_char revision = 0; + int i = 0; + unsigned int size = 0; + int force_detect = 0; + + if( MCA_bus == 0 ) { + return ENODEV; + } + + if( dev->mem_start == 1 ) { + force_detect = 1; + } + + /* search through the slots for the 3c523. */ + slot = mca_find_adapter( ELMC_MCA_ID, 0 ); + while( slot != -1 ) { + status = mca_read_stored_pos( slot, 2 ); + + /* + If we're trying to match a specified irq or IO address, + we'll reject a match unless it's what we're looking for. + */ + if( base_addr || irq ) { + /* we're looking for a card at a particular place */ + + if( irq && + irq != irq_table[ (status & ELMC_STATUS_IRQ_SELECT) >> 6] + ) { + slot = mca_find_adapter( ELMC_MCA_ID, slot + 1 ); + continue; + } + + if( base_addr && base_addr != + csr_table[ (status & ELMC_STATUS_CSR_SELECT) >> 1] + ) { + slot = mca_find_adapter( ELMC_MCA_ID, slot + 1 ); + continue; + } + } + + /* found what we're looking for... */ + break; + } + + /* we didn't find any 3c523 in the slots we checked for */ + if( slot == MCA_NOTFOUND ) { + return ((base_addr || irq) ? ENXIO : ENODEV); + } + + mca_set_adapter_name( slot, "3Com 3c523 Etherlink/MC" ); + mca_set_adapter_procfn( slot, (MCA_ProcFn) elmc_getinfo, dev ); + + /* if we get this far, adapter has been found - carry on */ + printk("%s: 3c523 adapter found in slot %d\n", dev->name, slot + 1); + + /* Now we extract configuration info from the card. + The 3c523 provides information in two of the POS registers, but + the second one is only needed if we want to tell the card what IRQ + to use. I suspect that whoever sets the thing up initially would + prefer we don't screw with those things. + + Note we we read the status info when we found the card... + + See 3c523.h for more details. + */ + + /* revision is stored in the first 4 bits of the revision register */ + revision = inb( dev->base_addr + ELMC_REVISION ) & 0xf; + + /* figure out our irq */ + dev->irq = irq_table[ (status & ELMC_STATUS_IRQ_SELECT) >> 6]; + + /* according to docs, we read the interrupt and write it back to + the IRQ select register, since the POST might not configure the IRQ + properly. */ + switch( dev->irq ) { + case 3: + mca_write_pos( slot, 3, 0x04 ); + break; + case 7: + mca_write_pos( slot, 3, 0x02 ); + break; + case 9: + mca_write_pos( slot, 3, 0x08 ); + break; + case 12: + mca_write_pos( slot, 3, 0x01 ); + break; + } + + /* Our IO address? */ + dev->base_addr = csr_table[ (status & ELMC_STATUS_CSR_SELECT) >> 1]; + + request_region( dev->base_addr, ELMC_IO_EXTENT,"3c523"); + + dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); + if (dev->priv == NULL) { + return -ENOMEM; + } + memset((char *) dev->priv,0,sizeof(struct priv)); + + ((struct priv *) (dev->priv))->slot = slot; + + printk("%s: 3Com 3c523 Rev 0x%x at %#lx\n", dev->name, (int) revision, + dev->base_addr); + + /* Determine if we're using the on-board transceiver (i.e. coax) or + an external one. The information is pretty much useless, but I + guess it's worth brownie points. */ + dev->if_port = (status & ELMC_STATUS_DISABLE_THIN); + + /* The 3c523 has a 24K chunk of memory. The first 16K is the + shared memory, while the last 8K is for the EtherStart BIOS ROM. + Which we don't care much about here. We'll just tell Linux that + we're using 16K. MCA won't permit adress space conflicts caused + by not mapping the other 8K. */ + dev->mem_start = (int)phys_to_virt(shm_table[ (status & ELMC_STATUS_MEMORY_SELECT) >> 3]); + + /* We're using MCA, so it's a given that the information about memory + size is correct. The Crynwr drivers do something like this. */ + + elmc_id_reset586(); /* seems like a good idea before checking it... */ + + size = 0x4000; /* check for 16K mem */ + if(!check586(dev,(char *) dev->mem_start,size)) { + /* + For some reason, the memory probe fails on some versions of the + card. However, the device works perfectly fine if you ignore that + little fact. Presumably, we're having trouble kicking the i82586, + but later attempts actually work. It's a ornery piece of silicon, + but this is getting silly... + */ + if( force_detect == 0 ) { + printk("%s: memprobe, Can't find memory at 0x%lx!\n", dev->name, + dev->mem_start ); + printk( "%s: try \"ether=0,0,1,eth0\" to force it\n", dev->name); + release_region( dev->base_addr, ELMC_IO_EXTENT ); + return ENODEV; + } else { + printk("%s: memory probe failed, forced detection is on", dev->name ); + } + } + dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */ + + ((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000; + alloc586(dev); + + elmc_id_reset586(); /* make sure it doesn't generate spurious ints */ + + /* set number of receive-buffs according to memsize */ + ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16; + + /* dump all the assorted information */ + printk("%s: IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->name, + dev->irq, dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1); + + /* The hardware address for the 3c523 is stored in the first six + bytes of the IO address. */ + printk( "%s: hardware address ", dev->name ); + for (i = 0; i < 6; i ++ ) { + dev->dev_addr[i] = inb(dev->base_addr + i); + printk(" %02x", dev->dev_addr[i]); + } + printk( "\n" ); + + dev->open = &elmc_open; + dev->stop = &elmc_close; + dev->get_stats = &elmc_get_stats; + dev->hard_start_xmit = &elmc_send_packet; + dev->set_multicast_list = &set_multicast_list; + + ether_setup(dev); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 0; + + /* note that we haven't actually requested the IRQ from the kernel. + That gets done in elmc_open(). I'm not sure that's such a good idea, + but it works, so I'll go with it. */ + + return 0; +} + +/********************************************** + * init the chip (elmc-interrupt should be disabled?!) + * needs a correct 'allocated' memory + */ + +static +int +init586(struct device *dev) { + void *ptr; + unsigned long s; + int i,result=0; + struct priv *p = (struct priv *) dev->priv; + volatile struct configure_cmd_struct *cfg_cmd; + volatile struct iasetup_cmd_struct *ias_cmd; + volatile struct tdr_cmd_struct *tdr_cmd; + volatile struct mcsetup_cmd_struct *mc_cmd; + struct dev_mc_list *dmi=dev->mc_list; + int num_addrs=dev->mc_count; + + ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct)); + + cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */ + cfg_cmd->cmd_status = 0; + cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST; + cfg_cmd->cmd_link = 0xffff; + + cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */ + cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */ + cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */ + cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */ + cfg_cmd->priority = 0x00; + cfg_cmd->ifs = 0x60; + cfg_cmd->time_low = 0x00; + cfg_cmd->time_high = 0xf2; + cfg_cmd->promisc = 0; + if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) + { + cfg_cmd->promisc=1; + dev->flags|=IFF_PROMISC; + } + cfg_cmd->carr_coll = 0x00; + + p->scb->cbl_offset = make16(cfg_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_id_attn586(); + + s = jiffies; /* warning: only active with interrupts on !! */ + while(!(cfg_cmd->cmd_status & STAT_COMPL)) { + if(jiffies-s > 30) break; + } + + if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK)) { + printk("%s (elmc): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status); + return 1; + } + + /* + * individual address setup + */ + ias_cmd = (struct iasetup_cmd_struct *)ptr; + + ias_cmd->cmd_status = 0; + ias_cmd->cmd_cmd = CMD_IASETUP | CMD_LAST; + ias_cmd->cmd_link = 0xffff; + + memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN); + + p->scb->cbl_offset = make16(ias_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_id_attn586(); + + s = jiffies; + while(!(ias_cmd->cmd_status & STAT_COMPL)) { + if(jiffies-s > 30) break; + } + + if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) { + printk("%s (elmc): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status); + return 1; + } + + /* + * TDR, wire check .. e.g. no resistor e.t.c + */ + tdr_cmd = (struct tdr_cmd_struct *)ptr; + + tdr_cmd->cmd_status = 0; + tdr_cmd->cmd_cmd = CMD_TDR | CMD_LAST; + tdr_cmd->cmd_link = 0xffff; + tdr_cmd->status = 0; + + p->scb->cbl_offset = make16(tdr_cmd); + + p->scb->cmd = CUC_START; /* cmd.-unit start */ + elmc_attn586(); + + s = jiffies; + while(!(tdr_cmd->cmd_status & STAT_COMPL)) { + if(jiffies - s > 30) { + printk("%s: %d Problems while running the TDR.\n",dev->name,__LINE__); + result = 1; + break; + } + } + + if(!result) { + DELAY(2); /* wait for result */ + result = tdr_cmd->status; + + p->scb->cmd = p->scb->status & STAT_MASK; + elmc_id_attn586(); /* ack the interrupts */ + + if(result & TDR_LNK_OK) { + /* empty */ + } else if(result & TDR_XCVR_PRB) { + printk("%s: TDR: Transceiver problem!\n",dev->name); + } else if(result & TDR_ET_OPN) { + printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK); + } else if(result & TDR_ET_SRT) { + if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */ + printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK); + } else { + printk("%s: TDR: Unknown status %04x\n",dev->name,result); + } + } + + /* + * ack interrupts + */ + p->scb->cmd = p->scb->status & STAT_MASK; + elmc_id_attn586(); + + /* + * alloc nop/xmit-cmds + */ +#if (NUM_XMIT_BUFFS == 1) + for(i=0;i<2;i++) { + p->nop_cmds[i] = (struct nop_cmd_struct *)ptr; + p->nop_cmds[i]->cmd_cmd = CMD_NOP; + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + } + p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */ + ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); +#else + for(i=0;inop_cmds[i] = (struct nop_cmd_struct *)ptr; + p->nop_cmds[i]->cmd_cmd = CMD_NOP; + p->nop_cmds[i]->cmd_status = 0; + p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i])); + ptr = (char *) ptr + sizeof(struct nop_cmd_struct); + p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/ + ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); + } +#endif + + ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ + + /* + * Multicast setup + */ + + if(dev->mc_count) { + /* I don't understand this: do we really need memory after the init? */ + int len = ((char *) p->iscp - (char *) ptr - 8) / 6; + if(len <= 0) { + printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name); + } else { + if(len < num_addrs) { + num_addrs = len; + printk("%s: Sorry, can only apply %d MC-Address(es).\n", + dev->name, num_addrs); + } + mc_cmd = (struct mcsetup_cmd_struct *) ptr; + mc_cmd->cmd_status = 0; + mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST; + mc_cmd->cmd_link = 0xffff; + mc_cmd->mc_cnt = num_addrs * 6; + for(i=0;imc_list[i], dmi->dmi_addr,6); + dmi=dmi->next; + } + p->scb->cbl_offset = make16(mc_cmd); + p->scb->cmd = CUC_START; + elmc_id_attn586(); + s = jiffies; + while(!(mc_cmd->cmd_status & STAT_COMPL)) { + if(jiffies - s > 30) + break; + } + if(!(mc_cmd->cmd_status & STAT_COMPL)) { + printk("%s: Can't apply multicast-address-list.\n",dev->name); + } + } + } + + /* + * alloc xmit-buffs / init xmit_cmds + */ + for(i=0;ixmit_cbuffs[i] = (char *)ptr; /* char-buffs */ + ptr = (char *) ptr + XMIT_BUFF_SIZE; + p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */ + ptr = (char *) ptr + sizeof(struct tbd_struct); + if((void *)ptr > (void *)p->iscp) { + printk("%s: not enough shared-mem for your configuration!\n",dev->name); + return 1; + } + memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct)); + memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct)); + p->xmit_cmds[i]->cmd_status = STAT_COMPL; + p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT; + p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i])); + p->xmit_buffs[i]->next = 0xffff; + p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i])); + } + + p->xmit_count = 0; + p->xmit_last = 0; +#ifndef NO_NOPCOMMANDS + p->nop_point = 0; +#endif + + /* + * 'start transmitter' (nop-loop) + */ +#ifndef NO_NOPCOMMANDS + p->scb->cbl_offset = make16(p->nop_cmds[0]); + p->scb->cmd = CUC_START; + elmc_id_attn586(); + WAIT_4_SCB_CMD(); +#else + p->xmit_cmds[0]->cmd_link = 0xffff; + p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT; +#endif + + return 0; +} + +/****************************************************** + * This is a helper routine for elmc_rnr_int() and init586(). + * It sets up the Receive Frame Area (RFA). + */ + +static +void* +alloc_rfa(struct device *dev,void *ptr) { + volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr; + volatile struct rbd_struct *rbd; + int i; + struct priv *p = (struct priv *) dev->priv; + + memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs); + p->rfd_first = rfd; + + for(i = 0; i < p->num_recv_buffs; i++) { + rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs); + } + rfd[p->num_recv_buffs-1].last = RFD_SUSP; /* RU suspend */ + + ptr = (void *) (rfd + p->num_recv_buffs); + + rbd = (struct rbd_struct *) ptr; + ptr = (void *) (rbd + p->num_recv_buffs); + + /* clr descriptors */ + memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs); + + for(i=0;inum_recv_buffs;i++) { + rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs)); + rbd[i].size = RECV_BUFF_SIZE; + rbd[i].buffer = make24(ptr); + ptr = (char *) ptr + RECV_BUFF_SIZE; + } + + p->rfd_top = p->rfd_first; + p->rfd_last = p->rfd_first + p->num_recv_buffs - 1; + + p->scb->rfa_offset = make16(p->rfd_first); + p->rfd_first->rbd_offset = make16(rbd); + + return ptr; +} + + +/************************************************** + * Interrupt Handler ... + */ + +static +void +elmc_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr) { + struct device *dev = (struct device *) dev_id; + unsigned short stat; + struct priv *p; + + if (dev == NULL) { + printk ("elmc-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2)); + return; + } else if( !dev->start ) { + /* The 3c523 has this habit of generating interrupts during the + reset. I'm not sure if the ni52 has this same problem, but it's + really annoying if we haven't finished initializing it. I was + hoping all the elmc_id_* commands would disable this, but I + might have missed a few. */ + + elmc_id_attn586(); /* ack inter. and disable any more */ + return; + } else if( !(ELMC_CTRL_INT & inb( dev->base_addr+ELMC_CTRL )) ) { + /* wasn't this device */ + return; + } + + /* reading ELMC_CTRL also clears the INT bit. */ + + p = (struct priv *) dev->priv; + + dev->interrupt = 1; + + while((stat=p->scb->status & STAT_MASK)) { + p->scb->cmd = stat; + elmc_attn586(); /* ack inter. */ + + if(stat & STAT_CX) { + /* command with I-bit set complete */ + elmc_xmt_int(dev); + } + + if(stat & STAT_FR) { + /* received a frame */ + elmc_rcv_int(dev); + } + +#ifndef NO_NOPCOMMANDS + if(stat & STAT_CNA) { + /* CU went 'not ready' */ + if(dev->start) { + printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status); + } + } +#endif + + if(stat & STAT_RNR) { + /* RU went 'not ready' */ + + if(p->scb->status & RU_SUSPEND) { + /* special case: RU_SUSPEND */ + + WAIT_4_SCB_CMD(); + p->scb->cmd = RUC_RESUME; + elmc_attn586(); + } else { + printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status); + elmc_rnr_int(dev); + } + } + WAIT_4_SCB_CMD(); /* wait for ack. (elmc_xmt_int can be faster than ack!!) */ + if(p->scb->cmd) { /* timed out? */ + break; + } + } + + dev->interrupt = 0; +} + +/******************************************************* + * receive-interrupt + */ + +static +void +elmc_rcv_int(struct device *dev) { + int status; + unsigned short totlen; + struct sk_buff *skb; + struct rbd_struct *rbd; + struct priv *p = (struct priv *) dev->priv; + + for(;(status = p->rfd_top->status) & STAT_COMPL;) { + rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset); + + if(status & STAT_OK) /* frame received without error? */ + { + if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */ + { + totlen &= RBD_MASK; /* length of this frame */ + rbd->status = 0; + skb = (struct sk_buff *) dev_alloc_skb(totlen+2); + if(skb != NULL) { + skb->dev = dev; + skb_reserve(skb,2); /* 16 byte alignment */ + memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + p->stats.rx_packets++; + } else { + p->stats.rx_dropped++; + } + } else { + printk("%s: received oversized frame.\n",dev->name); + p->stats.rx_dropped++; + } + } else /* frame !(ok), only with 'save-bad-frames' */ { + printk("%s: oops! rfd-error-status: %04x\n",dev->name,status); + p->stats.rx_errors++; + } + p->rfd_top->status = 0; + p->rfd_top->last = RFD_SUSP; + p->rfd_last->last = 0; /* delete RU_SUSP */ + p->rfd_last = p->rfd_top; + p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */ + } +} + +/********************************************************** + * handle 'Receiver went not ready'. + */ + +static +void +elmc_rnr_int(struct device *dev) { + struct priv *p = (struct priv *) dev->priv; + + p->stats.rx_errors++; + + WAIT_4_SCB_CMD(); /* wait for the last cmd */ + p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */ + elmc_attn586(); + WAIT_4_SCB_CMD(); /* wait for accept cmd. */ + + alloc_rfa(dev,(char *)p->rfd_first); + startrecv586(dev); /* restart RU */ + + printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status); + +} + +/********************************************************** + * handle xmit - interrupt + */ + +static +void +elmc_xmt_int(struct device *dev) { + int status; + struct priv *p = (struct priv *) dev->priv; + + status = p->xmit_cmds[p->xmit_last]->cmd_status; + if(!(status & STAT_COMPL)) { + printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name); + } + + if(status & STAT_OK) { + p->stats.tx_packets++; + p->stats.collisions += (status & TCMD_MAXCOLLMASK); + } else { + p->stats.tx_errors++; + if(status & TCMD_LATECOLL) { + printk("%s: late collision detected.\n",dev->name); + p->stats.collisions++; + } else if(status & TCMD_NOCARRIER) { + p->stats.tx_carrier_errors++; + printk("%s: no carrier detected.\n",dev->name); + } else if(status & TCMD_LOSTCTS) { + printk("%s: loss of CTS detected.\n",dev->name); + } else if(status & TCMD_UNDERRUN) { + p->stats.tx_fifo_errors++; + printk("%s: DMA underrun detected.\n",dev->name); + } else if(status & TCMD_MAXCOLL) { + printk("%s: Max. collisions exceeded.\n",dev->name); + p->stats.collisions += 16; + } + } + +#if (NUM_XMIT_BUFFS != 1) + if( (++p->xmit_last) == NUM_XMIT_BUFFS) { + p->xmit_last = 0; + } +#endif + + dev->tbusy = 0; + mark_bh(NET_BH); +} + +/*********************************************************** + * (re)start the receiver + */ + +static +void +startrecv586(struct device *dev) +{ + struct priv *p = (struct priv *) dev->priv; + + p->scb->rfa_offset = make16(p->rfd_first); + p->scb->cmd = RUC_START; + elmc_attn586(); /* start cmd. */ + WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */ +} + +/****************************************************** + * send frame + */ + +static +int +elmc_send_packet(struct sk_buff *skb, struct device *dev) +{ + int len; +#ifndef NO_NOPCOMMANDS + int next_nop; +#endif + struct priv *p = (struct priv *) dev->priv; + + if(dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) { + return 1; + } + + /* COMMAND-UNIT active? */ + if(p->scb->status & CU_ACTIVE) { + dev->tbusy = 0; +#ifdef DEBUG + printk("%s: strange ... timeout with CU active?!?\n",dev->name); + printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point); +#endif + p->scb->cmd = CUC_ABORT; + elmc_attn586(); + WAIT_4_SCB_CMD(); + p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]); + p->scb->cmd = CUC_START; + elmc_attn586(); + WAIT_4_SCB_CMD(); + dev->trans_start = jiffies; + return 0; + } else { +#ifdef DEBUG + printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status); + printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status); +#endif + elmc_close(dev); + elmc_open(dev); + } + dev->trans_start = jiffies; + return 0; + } + + if(skb == NULL) { + dev_tint(dev); + return 0; + } + + if (skb->len <= 0) { + return 0; + } + if(skb->len > XMIT_BUFF_SIZE) { + printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len); + return 0; + } + + if (set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + } else { + memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); + len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + +#if (NUM_XMIT_BUFFS == 1) +# ifdef NO_NOPCOMMANDS + p->xmit_buffs[0]->size = TBD_LAST | len; + for(i=0;i<16;i++) { + p->scb->cbl_offset = make16(p->xmit_cmds[0]); + p->scb->cmd = CUC_START; + p->xmit_cmds[0]->cmd_status = 0; + + elmc_attn586(); + dev->trans_start = jiffies; + if(!i) { + dev_kfree_skb(skb,FREE_WRITE); + } + WAIT_4_SCB_CMD(); + if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */ + { + break; + } + if(p->xmit_cmds[0]->cmd_status) { + break; + } + if(i==15) { + printk("%s: Can't start transmit-command.\n",dev->name); + } + } +# else + next_nop = (p->nop_point + 1) & 0x1; + p->xmit_buffs[0]->size = TBD_LAST | len; + + p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link + = make16((p->nop_cmds[next_nop])); + p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0; + + p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0])); + dev->trans_start = jiffies; + p->nop_point = next_nop; + dev_kfree_skb(skb,FREE_WRITE); +# endif +#else + p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len; + if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS ) { + next_nop = 0; + } + + p->xmit_cmds[p->xmit_count]->cmd_status = 0; + p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link + = make16((p->nop_cmds[next_nop])); + p->nop_cmds[next_nop]->cmd_status = 0; + + p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count])); + dev->trans_start = jiffies; + p->xmit_count = next_nop; + + cli(); + if(p->xmit_count != p->xmit_last) { + dev->tbusy = 0; + } + sti(); + dev_kfree_skb(skb,FREE_WRITE); +#endif + } + return 0; +} + +/******************************************* + * Someone wanna have the statistics + */ + +static +struct enet_statistics* +elmc_get_stats( struct device *dev ) { + struct priv *p = (struct priv *) dev->priv; + unsigned short crc,aln,rsc,ovrn; + + crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */ + p->scb->crc_errs -= crc; + aln = p->scb->aln_errs; + p->scb->aln_errs -= aln; + rsc = p->scb->rsc_errs; + p->scb->rsc_errs -= rsc; + ovrn = p->scb->ovrn_errs; + p->scb->ovrn_errs -= ovrn; + + p->stats.rx_crc_errors += crc; + p->stats.rx_fifo_errors += ovrn; + p->stats.rx_frame_errors += aln; + p->stats.rx_dropped += rsc; + + return &p->stats; +} + +/******************************************************** + * Set MC list .. + */ + +static +void +set_multicast_list(struct device *dev) { + if(!dev->start) { + /* without a running interface, promiscuous doesn't work */ + return; + } + + dev->start = 0; + alloc586(dev); + init586(dev); + startrecv586(dev); + dev->start = 1; +} + +/*************************************************************************/ + +#ifdef MODULE +char kernel_version[] = UTS_RELEASE; +static struct device dev_elmc = { + " " /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe }; + + +int irq=0; +int io=0; + +int +init_module(void) { + struct device *dev = &dev_elmc; + + dev->base_addr=io; + dev->irq=irq; + if (register_netdev(dev) != 0) { + return -EIO; + } + + return 0; +} + +void +cleanup_module(void) { + struct device *dev = &dev_elmc; + + if (MOD_IN_USE) { + printk("3c523: device busy, remove delayed\n"); + } else { + /* shutdown interrupts on the card */ + elmc_id_reset586(); + + if( dev->irq != 0 ) { + /* this should be done by close, but if we failed to + initialize properly something may have gotten hosed. */ + free_irq( dev->irq, dev ); + dev->irq = 0; + } + + + + if( dev->base_addr != 0 ) { + release_region( dev->base_addr, ELMC_IO_EXTENT ); + dev->base_addr = 0; + } + irq = 0; + io = 0; + + unregister_netdev(dev); + + mca_set_adapter_procfn( ((struct priv *) (dev->priv))->slot, + NULL, NULL ); + + kfree_s(dev->priv,sizeof(struct priv)); + dev->priv=NULL; + } +} +#endif /* MODULE */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/3c523.h linux-2.0.35-mca/drivers/net/3c523.h --- linux/drivers/net/3c523.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/3c523.h Sat Aug 8 22:20:47 1998 @@ -0,0 +1,355 @@ +#ifndef _3c523_INCLUDE_ +#define _3c523_INCLUDE_ +/* + This is basically a hacked version of ni52.h, for the 3c523 + Etherlink/MC. +*/ + +/* + * Intel i82586 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + * + * Copyright 1995 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca) + * + * See 3c523.c for details. + * + * $Header: /home/chrisb/linux-1.2.13-3c523/drivers/net/RCS/3c523.h,v 1.6 1996/01/20 05:09:00 chrisb Exp chrisb $ + */ + +/* + * where to find the System Configuration Pointer (SCP) + */ +#define SCP_DEFAULT_ADDRESS 0xfffff4 + + +/* + * System Configuration Pointer Struct + */ + +struct scp_struct +{ + unsigned short zero_dum0; /* has to be zero */ + unsigned char sysbus; /* 0=16Bit,1=8Bit */ + unsigned char zero_dum1; /* has to be zero for 586 */ + unsigned short zero_dum2; + unsigned short zero_dum3; + char *iscp; /* pointer to the iscp-block */ +}; + + +/* + * Intermediate System Configuration Pointer (ISCP) + */ +struct iscp_struct +{ + unsigned char busy; /* 586 clears after successful init */ + unsigned char zero_dummy; /* hast to be zero */ + unsigned short scb_offset; /* pointeroffset to the scb_base */ + char *scb_base; /* base-address of all 16-bit offsets */ +}; + +/* + * System Control Block (SCB) + */ +struct scb_struct +{ + unsigned short status; /* status word */ + unsigned short cmd; /* command word */ + unsigned short cbl_offset; /* pointeroffset, command block list */ + unsigned short rfa_offset; /* pointeroffset, receive frame area */ + unsigned short crc_errs; /* CRC-Error counter */ + unsigned short aln_errs; /* allignmenterror counter */ + unsigned short rsc_errs; /* Resourceerror counter */ + unsigned short ovrn_errs; /* OVerrunerror counter */ +}; + +/* + * possible command values for the command word + */ +#define RUC_MASK 0x0070 /* mask for RU commands */ +#define RUC_NOP 0x0000 /* NOP-command */ +#define RUC_START 0x0010 /* start RU */ +#define RUC_RESUME 0x0020 /* resume RU after suspend */ +#define RUC_SUSPEND 0x0030 /* suspend RU */ +#define RUC_ABORT 0x0040 /* abort receiver operation immediately */ + +#define CUC_MASK 0x0700 /* mask for CU command */ +#define CUC_NOP 0x0000 /* NOP-command */ +#define CUC_START 0x0100 /* start execution of 1. cmd on the CBL */ +#define CUC_RESUME 0x0200 /* resume after suspend */ +#define CUC_SUSPEND 0x0300 /* Suspend CU */ +#define CUC_ABORT 0x0400 /* abort command operation immediately */ + +#define ACK_MASK 0xf000 /* mask for ACK command */ +#define ACK_CX 0x8000 /* acknowledges STAT_CX */ +#define ACK_FR 0x4000 /* ack. STAT_FR */ +#define ACK_CNA 0x2000 /* ack. STAT_CNA */ +#define ACK_RNR 0x1000 /* ack. STAT_RNR */ + +/* + * possible status values for the status word + */ +#define STAT_MASK 0xf000 /* mask for cause of interrupt */ +#define STAT_CX 0x8000 /* CU finished cmd with its I bit set */ +#define STAT_FR 0x4000 /* RU finished receiving a frame */ +#define STAT_CNA 0x2000 /* CU left active state */ +#define STAT_RNR 0x1000 /* RU left ready state */ + +#define CU_STATUS 0x700 /* CU status, 0=idle */ +#define CU_SUSPEND 0x100 /* CU is suspended */ +#define CU_ACTIVE 0x200 /* CU is active */ + +#define RU_STATUS 0x70 /* RU status, 0=idle */ +#define RU_SUSPEND 0x10 /* RU suspended */ +#define RU_NOSPACE 0x20 /* RU no resources */ +#define RU_READY 0x40 /* RU is ready */ + +/* + * Receive Frame Descriptor (RFD) + */ +struct rfd_struct +{ + unsigned short status; /* status word */ + unsigned short last; /* Bit15,Last Frame on List / Bit14,suspend */ + unsigned short next; /* linkoffset to next RFD */ + unsigned short rbd_offset; /* pointeroffset to RBD-buffer */ + unsigned char dest[6]; /* ethernet-address, destination */ + unsigned char source[6]; /* ethernet-address, source */ + unsigned short length; /* 802.3 frame-length */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RFD_LAST 0x8000 /* last: last rfd in the list */ +#define RFD_SUSP 0x4000 /* last: suspend RU after */ +#define RFD_ERRMASK 0x0fe1 /* status: errormask */ +#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA */ +#define RFD_RNR 0x0200 /* status: receiver out of resources */ + +/* + * Receive Buffer Descriptor (RBD) + */ +struct rbd_struct +{ + unsigned short status; /* status word,number of used bytes in buff */ + unsigned short next; /* pointeroffset to next RBD */ + char *buffer; /* receive buffer address pointer */ + unsigned short size; /* size of this buffer */ + unsigned short zero_dummy; /* dummy */ +}; + +#define RBD_LAST 0x8000 /* last buffer */ +#define RBD_USED 0x4000 /* this buffer has data */ +#define RBD_MASK 0x3fff /* size-mask for length */ + +/* + * Statusvalues for Commands/RFD + */ +#define STAT_COMPL 0x8000 /* status: frame/command is complete */ +#define STAT_BUSY 0x4000 /* status: frame/command is busy */ +#define STAT_OK 0x2000 /* status: frame/command is ok */ + +/* + * Action-Commands + */ +#define CMD_NOP 0x0000 /* NOP */ +#define CMD_IASETUP 0x0001 /* initial address setup command */ +#define CMD_CONFIGURE 0x0002 /* configure command */ +#define CMD_MCSETUP 0x0003 /* MC setup command */ +#define CMD_XMIT 0x0004 /* transmit command */ +#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */ +#define CMD_DUMP 0x0006 /* dump command */ +#define CMD_DIAGNOSE 0x0007 /* diagnose command */ + +/* + * Action command bits + */ +#define CMD_LAST 0x8000 /* indicates last command in the CBL */ +#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */ +#define CMD_INT 0x2000 /* generate interrupt after execution */ + +/* + * NOP - command + */ +struct nop_cmd_struct +{ + unsigned short cmd_status; /* status of this command */ + unsigned short cmd_cmd; /* the command itself (+bits) */ + unsigned short cmd_link; /* offsetpointer to next command */ +}; + +/* + * IA Setup command + */ +struct iasetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char iaddr[6]; +}; + +/* + * Configure command + */ +struct configure_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned char byte_cnt; /* size of the config-cmd */ + unsigned char fifo; /* fifo/recv monitor */ + unsigned char sav_bf; /* save bad frames (bit7=1)*/ + unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/ + unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */ + unsigned char ifs; /* inter frame spacing */ + unsigned char time_low; /* slot time low */ + unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */ + unsigned char promisc; /* promisc-mode(0) , et al (1-7) */ + unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */ + unsigned char fram_len; /* minimal frame len */ + unsigned char dummy; /* dummy */ +}; + +/* + * Multicast Setup command + */ +struct mcsetup_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short mc_cnt; /* number of bytes in the MC-List */ + unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */ +}; + +/* + * transmit command + */ +struct transmit_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short tbd_offset; /* pointeroffset to TBD */ + unsigned char dest[6]; /* destination address of the frame */ + unsigned short length; /* user defined: 802.3 length / Ether type */ +}; + +#define TCMD_ERRMASK 0x0fa0 +#define TCMD_MAXCOLLMASK 0x000f +#define TCMD_MAXCOLL 0x0020 +#define TCMD_HEARTBEAT 0x0040 +#define TCMD_DEFERRED 0x0080 +#define TCMD_UNDERRUN 0x0100 +#define TCMD_LOSTCTS 0x0200 +#define TCMD_NOCARRIER 0x0400 +#define TCMD_LATECOLL 0x0800 + +struct tdr_cmd_struct +{ + unsigned short cmd_status; + unsigned short cmd_cmd; + unsigned short cmd_link; + unsigned short status; +}; + +#define TDR_LNK_OK 0x8000 /* No link problem identified */ +#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */ +#define TDR_ET_OPN 0x2000 /* open, no correct termination */ +#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */ +#define TDR_TIMEMASK 0x07ff /* mask for the time field */ + +/* + * Transmit Buffer Descriptor (TBD) + */ +struct tbd_struct +{ + unsigned short size; /* size + EOF-Flag(15) */ + unsigned short next; /* pointeroffset to next TBD */ + char *buffer; /* pointer to buffer */ +}; + +#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */ + +/*************************************************************************/ +/* +Verbatim from the Crynwyr stuff: + + The 3c523 responds with adapter code 0x6042 at slot +registers xxx0 and xxx1. The setup register is at xxx2 and +contains the following bits: + +0: card enable +2,1: csr address select + 00 = 0300 + 01 = 1300 + 10 = 2300 + 11 = 3300 +4,3: shared memory address select + 00 = 0c0000 + 01 = 0c8000 + 10 = 0d0000 + 11 = 0d8000 +5: set to disable on-board thinnet +7,6: (read-only) shows selected irq + 00 = 12 + 01 = 7 + 10 = 3 + 11 = 9 + +The interrupt-select register is at xxx3 and uses one bit per irq. + +0: int 12 +1: int 7 +2: int 3 +3: int 9 + + Again, the documentation stresses that the setup register +should never be written. The interrupt-select register may be +written with the value corresponding to bits 7.6 in +the setup register to insure corret setup. +*/ + +/* Offsets from the base I/O address. */ +#define ELMC_SA 0 /* first 6 bytes are IEEE network address */ +#define ELMC_CTRL 6 /* control & status register */ +#define ELMC_REVISION 7 /* revision register, first 4 bits only */ +#define ELMC_IO_EXTENT 8 + +/* these are the bit selects for the port register 2 */ +#define ELMC_STATUS_ENABLED 0x01 +#define ELMC_STATUS_CSR_SELECT 0x06 +#define ELMC_STATUS_MEMORY_SELECT 0x18 +#define ELMC_STATUS_DISABLE_THIN 0x20 +#define ELMC_STATUS_IRQ_SELECT 0xc0 + +/* this is the card id used in the detection code. You might recognize +it from @6042.adf */ +#define ELMC_MCA_ID 0x6042 + +/* + The following define the bits for the control & status register + + The bank select registers can be used if more than 16K of memory is + on the card. For some stupid reason, bank 3 is the one for the + bottom 16K, and the card defaults to bank 0. So we have to set the + bank to 3 before the card will even think of operating. To get bank + 3, set BS0 and BS1 to high (of course...) +*/ +#define ELMC_CTRL_BS0 0x01 /* RW bank select */ +#define ELMC_CTRL_BS1 0x02 /* RW bank select */ +#define ELMC_CTRL_INTE 0x04 /* RW interrupt enable, assert high */ +#define ELMC_CTRL_INT 0x08 /* R interrupt active, assert high */ +/*#define ELMC_CTRL_* 0x10*/ /* reserved */ +#define ELMC_CTRL_LBK 0x20 /* RW loopback enable, assert high */ +#define ELMC_CTRL_CA 0x40 /* RW channel attention, assert high */ +#define ELMC_CTRL_RST 0x80 /* RW 82586 reset, assert low */ + +/* some handy compound bits */ + +/* normal operation should have bank 3 and RST high, ints enabled */ +#define ELMC_NORMAL (ELMC_CTRL_INTE|ELMC_CTRL_RST|0x3) + +#endif /* _3c523_INCLUDE_ */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/Config.in linux-2.0.35-mca/drivers/net/Config.in --- linux/drivers/net/Config.in Sat Aug 8 21:35:57 1998 +++ linux-2.0.35-mca/drivers/net/Config.in Sat Aug 8 22:20:47 1998 @@ -61,7 +61,7 @@ tristate '3c505 support' CONFIG_ELPLUS tristate '3c507 support' CONFIG_EL16 fi - tristate '3c509/3c579 support' CONFIG_EL3 + tristate '3c509/3c529(MCA)/3c579 support' CONFIG_EL3 tristate '3c515 ISA Fast EtherLink' CONFIG_3C515 tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX fi @@ -115,6 +115,12 @@ fi bool 'SK_G16 support' CONFIG_SK_G16 fi + bool 'MCA Ethernet cards' CONFIG_NET_MCA + if [ "$CONFIG_NET_MCA" = "y" ]; then + bool '3Com 3c523 support' CONFIG_ELMC + tristate 'SMC Ultra MCA support' CONFIG_ULTRA_MCA + bool 'NE/2 (ne2000 MCA version) support' CONFIG_NE2_MCA + fi bool 'EISA, VLB and other board controllers' CONFIG_NET_EISA if [ "$CONFIG_NET_EISA" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then @@ -146,4 +152,3 @@ bool ' Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH bool ' Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051 fi - diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/Makefile linux-2.0.35-mca/drivers/net/Makefile --- linux/drivers/net/Makefile Sat Aug 8 21:35:57 1998 +++ linux-2.0.35-mca/drivers/net/Makefile Sat Aug 8 22:20:47 1998 @@ -25,6 +25,7 @@ CONFIG_HDLCDRV_BUILTIN := CONFIG_HDLCDRV_MODULE := + ifeq ($(CONFIG_ISDN),y) ifeq ($(CONFIG_ISDN_PPP),y) CONFIG_SLHC_BUILTIN = y @@ -120,6 +121,16 @@ endif endif +ifeq ($(CONFIG_NE2_MCA),y) +L_OBJS += ne2.o +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_NE2_MCA),m) + CONFIG_8390_MODULE = y + M_OBJS += ne2.o + endif +endif + ifeq ($(CONFIG_HPLAN),y) L_OBJS += hp.o CONFIG_8390_BUILTIN = y @@ -140,14 +151,24 @@ endif endif -ifeq ($(CONFIG_ULTRA),y) -L_OBJS += smc-ultra.o -CONFIG_8390_BUILTIN = y -else - ifeq ($(CONFIG_ULTRA),m) - CONFIG_8390_MODULE = y - M_OBJS += smc-ultra.o - endif +ifeq ($(CONFIG_ULTRA_MCA),m) + CONFIG_8390_MODULE = y + M_OBJS += smc-mca.o +else + ifeq ($(CONFIG_ULTRA_MCA),y) + CONFIG_8390_BUILTIN = y + L_OBJS += smc-mca.o + endif +endif + +ifeq ($(CONFIG_ULTRA),m) + CONFIG_8390_MODULE = y + M_OBJS += smc-ultra.o +else + ifeq ($(CONFIG_ULTRA),y) + CONFIG_8390_BUILTIN = y + L_OBJS += smc-ultra.o + endif endif ifeq ($(CONFIG_ULTRA32),y) @@ -292,6 +313,14 @@ else ifeq ($(CONFIG_EL16),m) M_OBJS += 3c507.o + endif +endif + +ifeq ($(CONFIG_ELMC),y) +L_OBJS += 3c523.o +else + ifeq ($(CONFIG_ELMC),m) + M_OBJS += 3c523.o endif endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/Space.c linux-2.0.35-mca/drivers/net/Space.c --- linux/drivers/net/Space.c Sat Aug 8 21:35:58 1998 +++ linux-2.0.35-mca/drivers/net/Space.c Sat Aug 8 22:20:47 1998 @@ -41,10 +41,12 @@ extern int tulip_probe(struct device *dev); extern int hp100_probe(struct device *dev); extern int ultra_probe(struct device *dev); +extern int ultramca_probe(struct device *dev); extern int ultra32_probe(struct device *dev); extern int wd_probe(struct device *dev); extern int el2_probe(struct device *dev); extern int ne_probe(struct device *dev); +extern int ne2_probe(struct device *dev); extern int ne2k_pci_probe(struct device *dev); extern int hp_probe(struct device *dev); extern int hp_plus_probe(struct device *dev); @@ -65,6 +67,7 @@ extern int wavelan_probe(struct device *); #endif /* defined(CONFIG_WAVELAN) */ extern int el16_probe(struct device *); +extern int elmc_probe(struct device *); extern int elplus_probe(struct device *); extern int ac3200_probe(struct device *); extern int e2100_probe(struct device *); @@ -150,6 +153,9 @@ #if defined(CONFIG_ULTRA) && ultra_probe(dev) #endif +#if defined(CONFIG_ULTRA_MCA) + && ultramca_probe(dev) +#endif #if defined(CONFIG_SMC9194) && smc_init(dev) #endif @@ -174,6 +180,9 @@ #if defined(CONFIG_NE2000) && ne_probe(dev) #endif +#if defined(CONFIG_NE2_MCA) + && ne2_probe(dev) +#endif #ifdef CONFIG_AT1500 && at1500_probe(dev) #endif @@ -215,6 +224,9 @@ #endif /* defined(CONFIG_WAVELAN) */ #ifdef CONFIG_EL16 /* 3c507 */ && el16_probe(dev) +#endif +#ifdef CONFIG_ELMC /* 3c523 */ + && elmc_probe(dev) #endif #ifdef CONFIG_ELPLUS /* 3c505 */ && elplus_probe(dev) diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/eexpress.c linux-2.0.35-mca/drivers/net/eexpress.c --- linux/drivers/net/eexpress.c Tue Apr 8 11:47:45 1997 +++ linux-2.0.35-mca/drivers/net/eexpress.c Sat Aug 8 22:20:48 1998 @@ -1,93 +1,83 @@ -/* $Id: eexpress.c,v 1.13.2.2 1997/03/11 05:52:32 davem Exp $ +/* Intel EtherExpress 16 device driver for Linux * - * Intel EtherExpress device driver for Linux + * Written by John Sullivan, 1995 + * based on original code by Donald Becker, with changes by + * Alan Cox and Pauline Middelink. * - * Original version written 1993 by Donald Becker - * Modularized by Pauline Middelink - * Changed to support io= irq= by Alan Cox - * Reworked 1995 by John Sullivan - * More fixes by Philip Blundell + * Support for 8-bit mode by Zoltan Szilagyi + * + * Many modifications, and currently maintained, by + * Philip Blundell */ -/* - * The original EtherExpress driver was just about usable, but - * suffered from a long startup delay, a hard limit of 16k memory - * usage on the card (EtherExpress 16s have either 32k or 64k), - * and random locks under load. The last was particularly annoying - * and made running eXceed/W preferable to Linux/XFree. After hacking - * through the driver for a couple of days, I had fixed most of the - * card handling errors, at the expense of turning the code into - * a complete jungle, but still hadn't tracked down the lock-ups. - * I had hoped these would be an IP bug, but failed to reproduce them - * under other drivers, so decided to start from scratch and rewrite - * the driver cleanly. And here it is. +/* The EtherExpress 16 is a fairly simple card, based on a shared-memory + * design using the i82586 Ethernet coprocessor. It bears no relationship, + * as far as I know, to the similarly-named "EtherExpress Pro" range. + * + * Historically, Linux support for these cards has been very bad. However, + * things seem to be getting better slowly. + */ + +/* If your card is confused about what sort of interface it has (eg it + * persistently reports "10baseT" when none is fitted), running 'SOFTSET /BART' + * or 'SOFTSET /LISA' from DOS seems to help. + */ + +/* Here's the scoop on memory mapping. * - * It's still not quite there, but self-corrects a lot more problems. - * the 'CU wedged, resetting...' message shouldn't happen at all, but - * at least we recover. It still locks occasionally, any ideas welcome. + * There are three ways to access EtherExpress card memory: either using the + * shared-memory mapping, or using PIO through the dataport, or using PIO + * through the "shadow memory" ports. * - * The original startup delay experienced by some people was due to the - * first ARP request for the address of the default router getting lost. - * (mostly the reply we were getting back was arriving before our - * hardware address was set up, or before the configuration sequence - * had told the card NOT to strip of the frame header). If you a long - * startup delay, you may have lost this ARP request/reply, although - * the original cause has been fixed. However, it is more likely that - * you've just locked under this version. + * The shadow memory system works by having the card map some of its memory + * as follows: * - * The main changes are in the 586 initialization procedure (which was - * just broken before - the EExp is a strange beasty and needs careful - * handling) the receive buffer handling (we now use a non-terminating - * circular list of buffers, which stops the card giving us out-of- - * resources errors), and the transmit code. The driver is also more - * structured, and I have tried to keep the kernel interface separate - * from the hardware interface (although some routines naturally want - * to do both). + * (the low five bits of the SMPTR are ignored) * - * John Sullivan + * base+0x4000..400f memory at SMPTR+0..15 + * base+0x8000..800f memory at SMPTR+16..31 + * base+0xc000..c007 dubious stuff (memory at SMPTR+16..23 apparently) + * base+0xc008..c00f memory at 0x0008..0x000f * - * 18/5/95: + * This last set (the one at c008) is particularly handy because the SCB + * lives at 0x0008. So that set of ports gives us easy random access to data + * in the SCB without having to mess around setting up pointers and the like. + * We always use this method to access the SCB (via the scb_xx() functions). * - * The lock-ups seem to happen when you access card memory after a 586 - * reset. This happens only 1 in 12 resets, on a random basis, and - * completely locks the machine. As far as I can see there is no - * workaround possible - the only thing to be done is make sure we - * never reset the card *after* booting the kernel - once at probe time - * must be sufficient, and we'll just have to put up with that failing - * occasionally (or buy a new NIC). By the way, this looks like a - * definite card bug, since Intel's own driver for DOS does exactly the - * same. + * Dataport access works by aiming the appropriate (read or write) pointer + * at the first address you're interested in, and then reading or writing from + * the dataport. The pointers auto-increment after each transfer. We use + * this for data transfer. * - * This bug makes switching in and out of promiscuous mode a risky - * business, since we must do a 586 reset each time. + * We don't use the shared-memory system because it allegedly doesn't work on + * all cards, and because it's a bit more prone to go wrong (it's one more + * thing to configure...). */ -/* - * Sources: - * - * The original eexpress.c by Donald Becker - * Sources: the Crynwr EtherExpress driver source. - * the Intel Microcommunications Databook Vol.1 1990 - * - * wavelan.c and i82586.h - * This was invaluable for the complete '586 configuration details - * and command format. +/* Known bugs: * - * The Crynwr sources (again) - * Not as useful as the Wavelan driver, but then I had eexpress.c to - * go off. + * - The card seems to want to give us two interrupts every time something + * happens, where just one would be better. + */ + +/* * - * The Intel EtherExpress 16 ethernet card - * Provided the only reason I want to see a working etherexpress driver. - * A lot of fixes came from just observing how the card (mis)behaves when - * you prod it. + * Note by Zoltan Szilagyi 10-12-96: * + * I've succeeded in eliminating the "CU wedged" messages, and hence the + * lockups, which were only occuring with cards running in 8-bit mode ("force + * 8-bit operation" in Intel's SoftSet utility). This version of the driver + * sets the 82586 and the ASIC to 8-bit mode at startup; it also stops the + * CU before submitting a packet for transmission, and then restarts it as soon + * as the process of handing the packet is complete. This is definitely an + * unnecessary slowdown if the card is running in 16-bit mode; therefore one + * should detect 16-bit vs 8-bit mode from the EEPROM settings and act + * accordingly. In 8-bit mode with this bugfix I'm getting about 150 K/s for + * ftp's, which is significantly better than I get in DOS, so the overhead of + * stopping and restarting the CU with each transmit is not prohibitive in + * practice. */ - -static char version[] = -"eexpress.c: v0.10 04-May-95 John Sullivan \n" -" v0.14 19-May-96 Philip Blundell \n"; - + #include #include @@ -102,104 +92,132 @@ #include #include #include -#include #include #include +#ifndef __initfunc +#define __initfunc(__initarg) __initarg +#else +#include +#endif + #include #include #include #include -/* - * Not actually used yet - may be implemented when the driver has - * been debugged! - * - * Debug Level Driver Status - * 0 Final release - * 1 Beta test - * 2 - * 3 - * 4 Report timeouts & 586 errors (normal debug level) - * 5 Report all major events - * 6 Dump sent/received packet contents - * 7 Report function entry/exit - */ +#include /* for CONFIG_MCA */ +#include + +#define test_and_set_bit(val, addr) set_bit(val, addr) #ifndef NET_DEBUG #define NET_DEBUG 4 #endif -static unsigned int net_debug = NET_DEBUG; - -#undef F_DEB -#include "eth82586.h" +#include "eexpress.h" -#define PRIV(x) ((struct net_local *)(x)->priv) #define EEXP_IO_EXTENT 16 +#define net_device_stats enet_statistics + /* * Private data declarations */ -struct net_local +struct net_local { - struct enet_statistics stats; - unsigned long init_time; /* jiffies when eexp_hw_init586 called */ - unsigned short rx_first; /* first rx buf, same as RX_BUF_START */ - unsigned short rx_last; /* last rx buf */ - unsigned short tx_head; /* next free tx buf */ - unsigned short tx_reap; /* first in-use tx buf */ - unsigned short tx_tail; /* previous tx buf to tx_head */ - unsigned short tx_link; /* last known-executing tx buf */ - unsigned short last_tx_restart; /* set to tx_link when we restart the CU */ + struct net_device_stats stats; + unsigned long last_tx; /* jiffies when last transmit started */ + unsigned long init_time; /* jiffies when eexp_hw_init586 called */ + unsigned short rx_first; /* first rx buf, same as RX_BUF_START */ + unsigned short rx_last; /* last rx buf */ + unsigned short rx_ptr; /* first rx buf to look at */ + unsigned short tx_head; /* next free tx buf */ + unsigned short tx_reap; /* first in-use tx buf */ + unsigned short tx_tail; /* previous tx buf to tx_head */ + unsigned short tx_link; /* last known-executing tx buf */ + unsigned short last_tx_restart; /* set to tx_link when we + restart the CU */ unsigned char started; - unsigned char promisc; unsigned short rx_buf_start; unsigned short rx_buf_end; unsigned short num_tx_bufs; unsigned short num_rx_bufs; + unsigned char width; /* 0 for 16bit, 1 for 8bit */ + unsigned char was_promisc; + unsigned char old_mc_count; }; -unsigned short start_code[] = { - 0x0000, /* SCP: set bus to 16 bits */ - 0x0000,0x0000, /* junk */ - 0x0000,0x0000, /* address of ISCP (lo,hi) */ +/* This is the code and data that is downloaded to the EtherExpress card's + * memory at boot time. + */ +static unsigned short start_code[] = { +/* 0x0000 */ 0x0001, /* ISCP: busy - cleared after reset */ 0x0008,0x0000,0x0000, /* offset,address (lo,hi) of SCB */ 0x0000,0x0000, /* SCB: status, commands */ - 0x0000,0x0000, /* links to first command block, first receive descriptor */ + 0x0000,0x0000, /* links to first command block, + first receive descriptor */ 0x0000,0x0000, /* CRC error, alignment error counts */ 0x0000,0x0000, /* out of resources, overrun error counts */ 0x0000,0x0000, /* pad */ 0x0000,0x0000, - 0x0000,Cmd_Config, /* startup configure sequence, at 0x0020 */ +/* 0x20 -- start of 82586 CU program */ +#define CONF_LINK 0x20 + 0x0000,Cmd_Config, 0x0032, /* link to next command */ 0x080c, /* 12 bytes follow : fifo threshold=8 */ - 0x2e40, /* don't rx bad frames : SRDY/ARDY => ext. sync. : preamble len=8 - * take addresses from data buffers : 6 bytes/address */ - 0x6000, /* default backoff method & priority : interframe spacing = 0x60 */ - 0xf200, /* slot time=0x200 : max collision retry = 0xf */ - 0x0000, /* no HDLC : normal CRC : enable broadcast : disable promiscuous/multicast modes */ + 0x2e40, /* don't rx bad frames + * SRDY/ARDY => ext. sync. : preamble len=8 + * take addresses from data buffers + * 6 bytes/address + */ + 0x6000, /* default backoff method & priority + * interframe spacing = 0x60 */ + 0xf200, /* slot time=0x200 + * max collision retry = 0xf */ +#define CONF_PROMISC 0x2e + 0x0000, /* no HDLC : normal CRC : enable broadcast + * disable promiscuous/multicast modes */ 0x003c, /* minimum frame length = 60 octets) */ - 0x0000,Cmd_INT|Cmd_SetAddr, + 0x0000,Cmd_SetAddr, 0x003e, /* link to next command */ - 0x0000,0x0000,0x0000, /* hardware address placed here, 0x0038 */ - 0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */ - 0x003e, +#define CONF_HWADDR 0x38 + 0x0000,0x0000,0x0000, /* hardware address placed here */ - 0x0000 + 0x0000,Cmd_MCast, + 0x0076, /* link to next command */ +#define CONF_NR_MULTICAST 0x44 + 0x0000, /* number of multicast addresses */ +#define CONF_MULTICAST 0x46 + 0x0000, 0x0000, 0x0000, /* some addresses */ + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, + +#define CONF_DIAG_RESULT 0x76 + 0x0000, Cmd_Diag, + 0x007c, /* link to next command */ + + 0x0000,Cmd_TDR|Cmd_INT, + 0x0084, +#define CONF_TDR_RESULT 0x82 + 0x0000, + 0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */ + 0x0084 /* dummy link */ }; -#define CONF_LINK 0x0020 -#define CONF_HW_ADDR 0x0038 - /* maps irq number to EtherExpress magic value */ static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 }; @@ -207,32 +225,86 @@ * Prototypes for Linux interface */ -extern int express_probe(struct device *dev); -static int eexp_open (struct device *dev); -static int eexp_close(struct device *dev); -static struct enet_statistics *eexp_stats(struct device *dev); -static int eexp_xmit (struct sk_buff *buf, struct device *dev); +extern int express_probe(struct device *dev); +static int eexp_open(struct device *dev); +static int eexp_close(struct device *dev); +static struct net_device_stats *eexp_stats(struct device *dev); +static int eexp_xmit(struct sk_buff *buf, struct device *dev); -static void eexp_irq (int irq, void *dev_addr, struct pt_regs *regs); -static void eexp_set_multicast(struct device *dev); +static void eexp_irq(int irq, void *dev_addr, struct pt_regs *regs); +static void eexp_set_multicast(struct device *dev); /* * Prototypes for hardware access functions */ -static void eexp_hw_rx (struct device *dev); -static void eexp_hw_tx (struct device *dev, unsigned short *buf, unsigned short len); -static int eexp_hw_probe (struct device *dev,unsigned short ioaddr); -static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location); +static void eexp_hw_rx_pio(struct device *dev); +static void eexp_hw_tx_pio(struct device *dev, unsigned short *buf, + unsigned short len); +static int eexp_hw_probe(struct device *dev,unsigned short ioaddr); +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, + unsigned char location); static unsigned short eexp_hw_lasttxstat(struct device *dev); -static void eexp_hw_txrestart (struct device *dev); +static void eexp_hw_txrestart(struct device *dev); + +static void eexp_hw_txinit (struct device *dev); +static void eexp_hw_rxinit (struct device *dev); + +static void eexp_hw_init586 (struct device *dev); +static void eexp_setup_filter (struct device *dev); -static void eexp_hw_txinit (struct device *dev); -static void eexp_hw_rxinit (struct device *dev); +static char *eexp_ifmap[]={"AUI", "BNC", "RJ45"}; +enum eexp_iftype {AUI=0, BNC=1, TPE=2}; -static void eexp_hw_init586 (struct device *dev); -static void eexp_hw_ASICrst (struct device *dev); +#define STARTED_RU 2 +#define STARTED_CU 1 + +/* + * Primitive hardware access functions. + */ + +static inline unsigned short scb_status(struct device *dev) +{ + return inw(dev->base_addr + 0xc008); +} + +static inline unsigned short scb_rdcmd(struct device *dev) +{ + return inw(dev->base_addr + 0xc00a); +} + +static inline void scb_command(struct device *dev, unsigned short cmd) +{ + outw(cmd, dev->base_addr + 0xc00a); +} + +static inline void scb_wrcbl(struct device *dev, unsigned short val) +{ + outw(val, dev->base_addr + 0xc00c); +} + +static inline void scb_wrrfa(struct device *dev, unsigned short val) +{ + outw(val, dev->base_addr + 0xc00e); +} + +static inline void set_loopback(struct device *dev) +{ + outb(inb(dev->base_addr + Config) | 2, dev->base_addr + Config); +} + +static inline void clear_loopback(struct device *dev) +{ + outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config); +} + +static inline short int SHADOW(short int addr) +{ + addr &= 0x1f; + if (addr > 0xf) addr += 0x3ff0; + return addr + 0x4000; +} /* * Linux interface @@ -242,9 +314,69 @@ * checks for presence of EtherExpress card */ -int express_probe(struct device *dev) +/* MCA code by ZP Gu (zpg@castle.net) */ +__initfunc(int express_probe(struct device *dev)) { - unsigned short *port,ports[] = { 0x0300,0x0270,0x0320,0x0340,0 }; +#ifdef CONFIG_MCA + if (MCA_bus) { + static unsigned short mca_ioa_table[] = { + 0x270,0x260,0x250,0x240,0x230,0x220,0x210,0x200, + 0x370,0x360,0x350,0x340,0x330,0x320,0x310,0x300 + }; + static unsigned char mca_irq_table[] = { + 12, 9, 3, 4, 5, 10, 11, 15, + }; + unsigned short ioaddr; + unsigned char irq; + u_char pos2, pos3; + int slot = 0, status; + + while( slot != MCA_NOTFOUND ) { + slot = mca_find_unused_adapter( 0x628b, slot ); + + if( slot == MCA_NOTFOUND ) break; + + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + + ioaddr = mca_ioa_table[(short)(pos3&0xf)]; + irq = mca_irq_table[(pos3 >> 4) & 0x07]; + + /* probing for a card at a particular IO/IRQ */ + if( (dev->irq && dev->irq != irq) || + (dev->base_addr && dev->base_addr != ioaddr) ) { + slot++; /* probing next slot */ + continue; + } + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* no Auto Detect!!! */ + dev->if_port = ((pos2&0x07) == 1) ? + AUI : ((pos3&0x80) ? TPE : BNC); + + printk("%s: found EtherExpress MC at addr=%#x,irq=%d,slot=%d\n", + dev->name,ioaddr,irq, slot+1); + + printk("%s: Please try non-shared IRQ and specify Connector Type explicitly\n", + dev->name); + printk("%s: in your IBM REFERENCE SETUP if EtherExpress MC does not work!\n", + dev->name); + + if (!(status = eexp_hw_probe(dev,ioaddr))) { + /* claim the slot */ + mca_set_adapter_name(slot, + "Intel EtherExpress(tm) MC Network Adapter"); + } + /* maybe we should look for the next one? */ + return status; + } + } else { +#endif + + unsigned short *port; + static unsigned short ports[] = { 0x300,0x310,0x270,0x320,0x340,0 }; unsigned short ioaddr = dev->base_addr; if (ioaddr&0xfe00) @@ -252,19 +384,23 @@ else if (ioaddr) return ENXIO; - for ( port=&ports[0] ; *port ; port++ ) + for (port=&ports[0] ; *port ; port++ ) { unsigned short sum = 0; int i; - for ( i=0 ; i<4 ; i++ ) + for ( i=0 ; i<4 ; i++ ) { unsigned short t; t = inb(*port + ID_PORT); sum |= (t>>4) << ((t & 0x03)<<2); } - if (sum==0xbaba && !eexp_hw_probe(dev,*port)) + if (sum==0xbaba && !eexp_hw_probe(dev,*port)) return 0; } + +#ifdef CONFIG_MCA + } +#endif return ENODEV; } @@ -276,23 +412,36 @@ { int irq = dev->irq; unsigned short ioaddr = dev->base_addr; + struct net_local *lp = (struct net_local *)dev->priv; #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: eexp_open()\n", dev->name); #endif - if (!irq || !irqrmap[irq]) + if (!irq || !irqrmap[irq]) return -ENXIO; - if (irq2dev_map[irq] || - /* more consistent, surely? */ - ((irq2dev_map[irq]=dev),0) || - request_irq(irq,&eexp_irq,0,"eexpress",NULL)) + if (request_irq(irq,&eexp_irq, +#ifdef CONFIG_MCA + (MCA_bus)?SA_SHIRQ:0, +#else + 0, +#endif + "EtherExpress",dev)) return -EAGAIN; - request_region(ioaddr, EEXP_IO_EXTENT, "eexpress"); + request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress"); + request_region(ioaddr+0x4000, 16, "EtherExpress shadow"); + request_region(ioaddr+0x8000, 16, "EtherExpress shadow"); + request_region(ioaddr+0xc000, 16, "EtherExpress shadow"); dev->tbusy = 0; dev->interrupt = 0; + + if (lp->width) { + printk("%s: forcing ASIC to 8-bit mode\n", dev->name); + outb(inb(dev->base_addr+Config)&~4, dev->base_addr+Config); + } + eexp_hw_init586(dev); dev->start = 1; MOD_INC_USE_COUNT; @@ -303,25 +452,27 @@ } /* - * close and disable the interface, leaving - * the 586 in reset + * close and disable the interface, leaving the 586 in reset. */ + static int eexp_close(struct device *dev) { unsigned short ioaddr = dev->base_addr; + struct net_local *lp = dev->priv; + int irq = dev->irq; - dev->tbusy = 1; + dev->tbusy = 1; dev->start = 0; - + outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); - PRIV(dev)->started = 0; - outw(SCB_CUsuspend|SCB_RUsuspend,ioaddr+SCB_CMD); + lp->started = 0; + scb_command(dev, SCB_CUsuspend|SCB_RUsuspend); outb(0,ioaddr+SIGNAL_CA); - free_irq(irq,NULL); - irq2dev_map[irq] = NULL; + free_irq(irq,dev); outb(i586_RST,ioaddr+EEPROM_Ctrl); release_region(ioaddr,16); + MOD_DEC_USE_COUNT; return 0; } @@ -330,142 +481,145 @@ * Return interface stats */ -static struct enet_statistics *eexp_stats(struct device *dev) +static struct net_device_stats *eexp_stats(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - /* - * Hmmm, this looks a little too easy... The card maintains - * some stats in the SCB, and I'm not convinced we're - * incrementing the most sensible statistics when the card - * returns an error (esp. slow DMA, out-of-resources) - */ return &lp->stats; } /* - * Called to transmit a packet, or to allow us to right ourselves - * if the kernel thinks we've died. + * This gets called when a higher level thinks we are broken. Check that + * nothing has become jammed in the CU. */ -static int eexp_xmit(struct sk_buff *buf, struct device *dev) +static void unstick_cu(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; unsigned short ioaddr = dev->base_addr; -#if NET_DEBUG > 6 - printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name); -#endif - - outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); - if (dev->tbusy) + if (lp->started) { - /* This will happen, but hopefully not as often as when - * tbusy==0. If it happens too much, we probably ought - * to think about unwedging ourselves... - */ - if (test_bit(0,(void *)&PRIV(dev)->started)) + if ((jiffies - dev->trans_start)>50) { - if ((jiffies - dev->trans_start)>5) + if (lp->tx_link==lp->last_tx_restart) { - if (lp->tx_link==lp->last_tx_restart) + unsigned short boguscount=200,rsst; + printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n", + dev->name, scb_status(dev)); + eexp_hw_txinit(dev); + lp->last_tx_restart = 0; + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); + while (!SCB_complete(rsst=scb_status(dev))) { - unsigned short boguscount=200,rsst; - printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n", - dev->name,inw(ioaddr+SCB_STATUS)); - eexp_hw_txinit(dev); - lp->last_tx_restart = 0; - outw(lp->tx_link,ioaddr+SCB_CBL); - outw(0,ioaddr+SCB_STATUS); - outw(SCB_CUstart,ioaddr+SCB_CMD); - outb(0,ioaddr+SIGNAL_CA); - while (!SCB_complete(rsst=inw(ioaddr+SCB_STATUS))) + if (!--boguscount) { - if (!--boguscount) - { - boguscount=200; - printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n", - dev->name,rsst); - outw(lp->tx_link,ioaddr+SCB_CBL); - outw(0,ioaddr+SCB_STATUS); - outw(SCB_CUstart,ioaddr+SCB_CMD); - outb(0,ioaddr+SIGNAL_CA); - } + boguscount=200; + printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n", + dev->name,rsst); + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); + outb(0,ioaddr+SIGNAL_CA); } - dev->tbusy = 0; - mark_bh(NET_BH); + } + dev->tbusy = 0; + mark_bh(NET_BH); + } + else + { + unsigned short status = scb_status(dev); + if (SCB_CUdead(status)) + { + unsigned short txstatus = eexp_hw_lasttxstat(dev); + printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n", + dev->name, status, txstatus); + eexp_hw_txrestart(dev); } else { - unsigned short status = inw(ioaddr+SCB_STATUS); - if (SCB_CUdead(status)) + unsigned short txstatus = eexp_hw_lasttxstat(dev); + if (dev->tbusy && !txstatus) { - unsigned short txstatus = eexp_hw_lasttxstat(dev); - printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n", - dev->name, status, txstatus); - eexp_hw_txrestart(dev); + printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n", + dev->name,status,txstatus); + eexp_hw_init586(dev); + dev->tbusy = 0; + mark_bh(NET_BH); } else { - unsigned short txstatus = eexp_hw_lasttxstat(dev); - if (dev->tbusy && !txstatus) - { - printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n", - dev->name,status,txstatus); - eexp_hw_init586(dev); - dev->tbusy = 0; - mark_bh(NET_BH); - } + printk(KERN_WARNING "%s: transmit timed out\n", dev->name); } } } } - else - { - if ((jiffies-lp->init_time)>10) - { - unsigned short status = inw(ioaddr+SCB_STATUS); - printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", - dev->name, status); - eexp_hw_init586(dev); - dev->tbusy = 0; - mark_bh(NET_BH); - } - } } - - if (buf==NULL) + else { - unsigned short status = inw(ioaddr+SCB_STATUS); - unsigned short txstatus = eexp_hw_lasttxstat(dev); - if (SCB_CUdead(status)) + if ((jiffies-lp->init_time)>10) { - printk(KERN_WARNING "%s: CU has died! status %04x %04x, attempting to restart...\n", - dev->name, status, txstatus); - lp->stats.tx_errors++; - eexp_hw_txrestart(dev); + unsigned short status = scb_status(dev); + printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", + dev->name, status); + eexp_hw_init586(dev); + dev->tbusy = 0; + mark_bh(NET_BH); } - dev_tint(dev); - outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); - dev_kfree_skb(buf, FREE_WRITE); - return 0; } +} - if (set_bit(0,(void *)&dev->tbusy)) +/* + * Called to transmit a packet, or to allow us to right ourselves + * if the kernel thinks we've died. + */ +static int eexp_xmit(struct sk_buff *buf, struct device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + +#if NET_DEBUG > 6 + printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name); +#endif + + outb(SIRQ_dis|irqrmap[dev->irq],dev->base_addr+SET_IRQ); + + /* If dev->tbusy is set, all our tx buffers are full but the kernel + * is calling us anyway. Check that nothing bad is happening. + */ + if (dev->tbusy) { + int status = scb_status(dev); + unstick_cu(dev); + if ((jiffies - lp->last_tx) < HZ) + return 1; + printk(KERN_INFO "%s: transmit timed out, %s?", dev->name, + (SCB_complete(status)?"lost interrupt": + "board on fire")); + lp->stats.tx_errors++; + dev->tbusy = 0; + lp->last_tx = jiffies; + if (!SCB_complete(status)) { + scb_command(dev, SCB_CUabort); + outb(0,dev->base_addr+SIGNAL_CA); + } + } + + if (test_and_set_bit(0,(void *)&dev->tbusy)) { lp->stats.tx_dropped++; } else { - unsigned short length = (ETH_ZLEN < buf->len) ? buf->len : ETH_ZLEN; + unsigned short length = (ETH_ZLEN < buf->len) ? buf->len : + ETH_ZLEN; unsigned short *data = (unsigned short *)buf->data; - - outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); - eexp_hw_tx(dev,data,length); - outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); +#ifdef NEW + lp->stats.tx_bytes += length; +#endif + eexp_hw_tx_pio(dev,data,length); } dev_kfree_skb(buf, FREE_WRITE); - outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); + outb(SIRQ_en|irqrmap[dev->irq],dev->base_addr+SET_IRQ); return 0; } @@ -476,81 +630,182 @@ * check to make sure we've not become wedged. */ +/* + * Handle an EtherExpress interrupt + * If we've finished initializing, start the RU and CU up. + * If we've already started, reap tx buffers, handle any received packets, + * check to make sure we've not become wedged. + */ + +static unsigned short eexp_start_irq(struct device *dev, + unsigned short status) +{ + unsigned short ack_cmd = SCB_ack(status); + struct net_local *lp = (struct net_local *)dev->priv; + unsigned short ioaddr = dev->base_addr; + if ((dev->flags & IFF_UP) && !(lp->started & STARTED_CU)) { + short diag_status, tdr_status; + while (SCB_CUstat(status)==2) + status = scb_status(dev); +#if NET_DEBUG > 4 + printk("%s: CU went non-active (status %04x)\n", + dev->name, status); +#endif + + outw(CONF_DIAG_RESULT & ~31, ioaddr + SM_PTR); + diag_status = inw(ioaddr + SHADOW(CONF_DIAG_RESULT)); + if (diag_status & 1<<11) { + printk(KERN_WARNING "%s: 82586 failed self-test\n", + dev->name); + } else if (!(diag_status & 1<<13)) { + printk(KERN_WARNING "%s: 82586 self-test failed to complete\n", dev->name); + } + + outw(CONF_TDR_RESULT & ~31, ioaddr + SM_PTR); + tdr_status = inw(ioaddr + SHADOW(CONF_TDR_RESULT)); + if (tdr_status & (TDR_SHORT|TDR_OPEN)) { + printk(KERN_WARNING "%s: TDR reports cable %s at %d tick%s\n", dev->name, (tdr_status & TDR_SHORT)?"short":"broken", tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : ""); + } + else if (tdr_status & TDR_XCVRPROBLEM) { + printk(KERN_WARNING "%s: TDR reports transceiver problem\n", dev->name); + } + else if (tdr_status & TDR_LINKOK) { +#if NET_DEBUG > 4 + printk(KERN_DEBUG "%s: TDR reports link OK\n", dev->name); +#endif + } else { + printk("%s: TDR is ga-ga (status %04x)\n", dev->name, + tdr_status); + } + + lp->started |= STARTED_CU; + scb_wrcbl(dev, lp->tx_link); + /* if the RU isn't running, start it now */ + if (!(lp->started & STARTED_RU)) { + ack_cmd |= SCB_RUstart; + scb_wrrfa(dev, lp->rx_buf_start); + lp->rx_ptr = lp->rx_buf_start; + } + ack_cmd |= SCB_CUstart | 0x2000; + } + + if ((dev->flags & IFF_UP) && !(lp->started & STARTED_RU) && SCB_RUstat(status)==4) + lp->started|=STARTED_RU; + + return ack_cmd; +} + +static void eexp_cmd_clear(struct device *dev) +{ + unsigned long int oldtime = jiffies; + while (scb_rdcmd(dev) && ((jiffies-oldtime)<10)); + if (scb_rdcmd(dev)) { + printk("%s: command didn't clear\n", dev->name); + } +} + static void eexp_irq(int irq, void *dev_info, struct pt_regs *regs) { - struct device *dev = irq2dev_map[irq]; + struct device *dev = dev_info; struct net_local *lp; unsigned short ioaddr,status,ack_cmd; - unsigned short old_rp,old_wp; + unsigned short old_read_ptr, old_write_ptr; - if (dev==NULL) + if (dev==NULL) { - printk(KERN_WARNING "net_interrupt(): irq %d for unknown device caught by EExpress\n",irq); + printk(KERN_WARNING "eexpress: irq %d for unknown device\n", + irq); return; } -#if NET_DEBUG > 6 - printk(KERN_DEBUG "%s: interrupt\n", dev->name); -#endif - - dev->interrupt = 1; /* should this be reset on exit? */ - lp = (struct net_local *)dev->priv; ioaddr = dev->base_addr; + status = scb_status(dev); + +#ifdef CONFIG_MCA + if (MCA_bus && !(status & 0xf000)) /* Any better way? */ + return; +#endif + + old_read_ptr = inw(ioaddr+READ_PTR); + old_write_ptr = inw(ioaddr+WRITE_PTR); + outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); - old_rp = inw(ioaddr+READ_PTR); - old_wp = inw(ioaddr+WRITE_PTR); - status = inw(ioaddr+SCB_STATUS); - ack_cmd = SCB_ack(status); - if (PRIV(dev)->started==0 && SCB_complete(status)) - { + dev->interrupt = 1; + #if NET_DEBUG > 4 - printk(KERN_DEBUG "%s: SCBcomplete event received\n", dev->name); + printk(KERN_DEBUG "%s: interrupt (status %x)\n", dev->name, status); #endif - while (SCB_CUstat(status)==2) - status = inw_p(ioaddr+SCB_STATUS); -#if NET_DEBUG > 4 - printk(KERN_DEBUG "%s: CU went non-active (status = %08x)\n", dev->name, status); + + if (lp->started == (STARTED_CU | STARTED_RU)) { + + do { + eexp_cmd_clear(dev); + + ack_cmd = SCB_ack(status); + scb_command(dev, ack_cmd); + outb(0,ioaddr+SIGNAL_CA); + + eexp_cmd_clear(dev); + + if (SCB_complete(status)) { + if (!eexp_hw_lasttxstat(dev)) { + printk("%s: tx interrupt but no status\n", dev->name); + } + } + + if (SCB_rxdframe(status)) + eexp_hw_rx_pio(dev); + + status = scb_status(dev); + } while (status & 0xc000); + + if (SCB_RUdead(status)) + { + printk(KERN_WARNING "%s: RU stopped: status %04x\n", + dev->name,status); +#if 0 + printk(KERN_WARNING "%s: cur_rfd=%04x, cur_rbd=%04x\n", dev->name, lp->cur_rfd, lp->cur_rbd); + outw(lp->cur_rfd, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: [%04x]\n", dev->name, inw(ioaddr+DATAPORT)); + outw(lp->cur_rfd+6, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: rbd is %04x\n", dev->name, rbd= inw(ioaddr+DATAPORT)); + outw(rbd, ioaddr+READ_PTR); + printk(KERN_WARNING "%s: [%04x %04x] ", dev->name, inw(ioaddr+DATAPORT), inw(ioaddr+DATAPORT)); + outw(rbd+8, ioaddr+READ_PTR); + printk("[%04x]\n", inw(ioaddr+DATAPORT)); #endif - PRIV(dev)->started=1; - outw_p(lp->tx_link,ioaddr+SCB_CBL); - outw_p(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA); - ack_cmd |= SCB_CUstart | SCB_RUstart; - } - else if (PRIV(dev)->started) - { - unsigned short txstatus; - txstatus = eexp_hw_lasttxstat(dev); - } - - if (SCB_rxdframe(status)) - { - eexp_hw_rx(dev); + lp->stats.rx_errors++; +#if 1 + eexp_hw_rxinit(dev); +#else + lp->cur_rfd = lp->first_rfd; +#endif + scb_wrrfa(dev, lp->rx_buf_start); + scb_command(dev, SCB_RUstart); + outb(0,ioaddr+SIGNAL_CA); + } + } else { + if (status & 0x8000) + ack_cmd = eexp_start_irq(dev, status); + else + ack_cmd = SCB_ack(status); + scb_command(dev, ack_cmd); + outb(0,ioaddr+SIGNAL_CA); } - if ((PRIV(dev)->started&2)!=0 && SCB_RUstat(status)!=4) - { - printk(KERN_WARNING "%s: RU stopped status %04x, restarting...\n", - dev->name,status); - lp->stats.rx_errors++; - eexp_hw_rxinit(dev); - outw(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA); - ack_cmd |= SCB_RUstart; - } - else if (PRIV(dev)->started==1 && SCB_RUstat(status)==4) - PRIV(dev)->started|=2; + eexp_cmd_clear(dev); + + outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ); - outw(ack_cmd,ioaddr+SCB_CMD); - outb(0,ioaddr+SIGNAL_CA); - outw(old_rp,ioaddr+READ_PTR); - outw(old_wp,ioaddr+WRITE_PTR); - outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ); dev->interrupt = 0; -#if NET_DEBUG > 6 - printk(KERN_DEBUG "%s: leaving eexp_irq()\n", dev->name); +#if NET_DEBUG > 6 + printk("%s: leaving eexp_irq()\n", dev->name); #endif + outw(old_read_ptr, ioaddr+READ_PTR); + outw(old_write_ptr, ioaddr+WRITE_PTR); return; } @@ -559,50 +814,79 @@ */ /* + * Set the cable type to use. + */ + +static void eexp_hw_set_interface(struct device *dev) +{ + unsigned char oldval = inb(dev->base_addr + 0x300e); + oldval &= ~0x82; + switch (dev->if_port) { + case TPE: + oldval |= 0x2; + case BNC: + oldval |= 0x80; + break; + } + outb(oldval, dev->base_addr+0x300e); + udelay(20000); +} + +/* * Check all the receive buffers, and hand any received packets * to the upper levels. Basic sanity check on each frame - * descriptor + * descriptor, though we don't bother trying to fix broken ones. */ - -static void eexp_hw_rx(struct device *dev) + +static void eexp_hw_rx_pio(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; - unsigned short old_wp = inw(ioaddr+WRITE_PTR); - unsigned short old_rp = inw(ioaddr+READ_PTR); - unsigned short rx_block = lp->rx_first; + unsigned short rx_block = lp->rx_ptr; unsigned short boguscount = lp->num_rx_bufs; + unsigned short ioaddr = dev->base_addr; + unsigned short status; #if NET_DEBUG > 6 printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name); #endif - while (outw(rx_block,ioaddr+READ_PTR),boguscount--) - { - unsigned short status = inw(ioaddr); - unsigned short rfd_cmd = inw(ioaddr); - unsigned short rx_next = inw(ioaddr); - unsigned short pbuf = inw(ioaddr); - unsigned short pkt_len; + do { + unsigned short rfd_cmd, rx_next, pbuf, pkt_len; + + outw(rx_block, ioaddr + READ_PTR); + status = inw(ioaddr + DATAPORT); - if (FD_Done(status)) + if (FD_Done(status)) { - outw(pbuf,ioaddr+READ_PTR); - pkt_len = inw(ioaddr); + rfd_cmd = inw(ioaddr + DATAPORT); + rx_next = inw(ioaddr + DATAPORT); + pbuf = inw(ioaddr + DATAPORT); + + outw(pbuf, ioaddr + READ_PTR); + pkt_len = inw(ioaddr + DATAPORT); - if (rfd_cmd!=0x0000 || pbuf!=rx_block+0x16 - || (pkt_len & 0xc000)!=0xc000) + if (rfd_cmd!=0x0000) + { + printk(KERN_WARNING "%s: rfd_cmd not zero:0x%04x\n", + dev->name, rfd_cmd); + continue; + } + else if (pbuf!=rx_block+0x16) { - printk(KERN_WARNING "%s: Rx frame at %04x corrupted, status %04x, cmd %04x, " - "next %04x, pbuf %04x, len %04x\n",dev->name,rx_block, - status,rfd_cmd,rx_next,pbuf,pkt_len); - boguscount++; + printk(KERN_WARNING "%s: rfd and rbd out of sync 0x%04x 0x%04x\n", + dev->name, rx_block+0x16, pbuf); continue; } - else if (!FD_OK(status)) + else if ((pkt_len & 0xc000)!=0xc000) + { + printk(KERN_WARNING "%s: EOF or F not set on received buffer (%04x)\n", + dev->name, pkt_len & 0xc000); + continue; + } + else if (!FD_OK(status)) { lp->stats.rx_errors++; - if (FD_CRC(status)) + if (FD_CRC(status)) lp->stats.rx_crc_errors++; if (FD_Align(status)) lp->stats.rx_frame_errors++; @@ -618,7 +902,7 @@ struct sk_buff *skb; pkt_len &= 0x3fff; skb = dev_alloc_skb(pkt_len+16); - if (skb == NULL) + if (skb == NULL) { printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name); lp->stats.rx_dropped++; @@ -626,167 +910,207 @@ } skb->dev = dev; skb_reserve(skb, 2); - outw(pbuf+10,ioaddr+READ_PTR); - insw(ioaddr,skb_put(skb,pkt_len),(pkt_len+1)>>1); + outw(pbuf+10, ioaddr+READ_PTR); + insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1); skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); lp->stats.rx_packets++; +#ifdef NEW + lp->stats.rx_bytes += pkt_len; +#endif } - outw(rx_block,ioaddr+WRITE_PTR); - outw(0x0000,ioaddr); - outw(0x0000,ioaddr); + outw(rx_block, ioaddr+WRITE_PTR); + outw(0, ioaddr+DATAPORT); + outw(0, ioaddr+DATAPORT); + rx_block = rx_next; } - rx_block = rx_next; - } - outw(old_rp,ioaddr+READ_PTR); - outw(old_wp,ioaddr+WRITE_PTR); + } while (FD_Done(status) && boguscount--); + lp->rx_ptr = rx_block; } /* * Hand a packet to the card for transmission * If we get here, we MUST have already checked * to make sure there is room in the transmit - * buffer region + * buffer region. */ -static void eexp_hw_tx(struct device *dev, unsigned short *buf, unsigned short len) +static void eexp_hw_tx_pio(struct device *dev, unsigned short *buf, + unsigned short len) { struct net_local *lp = (struct net_local *)dev->priv; unsigned short ioaddr = dev->base_addr; - unsigned short old_wp = inw(ioaddr+WRITE_PTR); - outw(lp->tx_head,ioaddr+WRITE_PTR); - outw(0x0000,ioaddr); - outw(Cmd_INT|Cmd_Xmit,ioaddr); - outw(lp->tx_head+0x08,ioaddr); - outw(lp->tx_head+0x0e,ioaddr); - outw(0x0000,ioaddr); - outw(0x0000,ioaddr); - outw(lp->tx_head+0x08,ioaddr); - outw(0x8000|len,ioaddr); - outw(-1,ioaddr); - outw(lp->tx_head+0x16,ioaddr); - outw(0,ioaddr); - outsw(ioaddr,buf,(len+1)>>1); - outw(lp->tx_tail+0x0c,ioaddr+WRITE_PTR); - outw(lp->tx_head,ioaddr); + if (lp->width) { + /* Stop the CU so that there is no chance that it + jumps off to a bogus address while we are writing the + pointer to the next transmit packet in 8-bit mode -- + this eliminates the "CU wedged" errors in 8-bit mode. + (Zoltan Szilagyi 10-12-96) */ + scb_command(dev, SCB_CUsuspend); + outw(0xFFFF, ioaddr+SIGNAL_CA); + } + + outw(lp->tx_head, ioaddr + WRITE_PTR); + + outw(0x0000, ioaddr + DATAPORT); + outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT); + outw(lp->tx_head+0x08, ioaddr + DATAPORT); + outw(lp->tx_head+0x0e, ioaddr + DATAPORT); + + outw(0x0000, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + outw(lp->tx_head+0x08, ioaddr + DATAPORT); + + outw(0x8000|len, ioaddr + DATAPORT); + outw(-1, ioaddr + DATAPORT); + outw(lp->tx_head+0x16, ioaddr + DATAPORT); + outw(0, ioaddr + DATAPORT); + + outsw(ioaddr + DATAPORT, buf, (len+1)>>1); + + outw(lp->tx_tail+0xc, ioaddr + WRITE_PTR); + outw(lp->tx_head, ioaddr + DATAPORT); + dev->trans_start = jiffies; lp->tx_tail = lp->tx_head; - if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) + if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) lp->tx_head = TX_BUF_START; - else + else lp->tx_head += TX_BUF_SIZE; - if (lp->tx_head != lp->tx_reap) + if (lp->tx_head != lp->tx_reap) dev->tbusy = 0; - outw(old_wp,ioaddr+WRITE_PTR); + + if (lp->width) { + /* Restart the CU so that the packet can actually + be transmitted. (Zoltan Szilagyi 10-12-96) */ + scb_command(dev, SCB_CUresume); + outw(0xFFFF, ioaddr+SIGNAL_CA); + } + + lp->stats.tx_packets++; + lp->last_tx = jiffies; } /* * Sanity check the suspected EtherExpress card - * Read hardware address, reset card, size memory and - * initialize buffer memory pointers. These should - * probably be held in dev->priv, in case someone has 2 - * differently configured cards in their box (Arghhh!) + * Read hardware address, reset card, size memory and initialize buffer + * memory pointers. These are held in dev->priv, in case someone has more + * than one card in a machine. */ - -static int eexp_hw_probe(struct device *dev, unsigned short ioaddr) +__initfunc(static int eexp_hw_probe(struct device *dev, unsigned short ioaddr)) { unsigned short hw_addr[3]; + unsigned char buswidth; + unsigned int memory_size; int i; - unsigned char *chw_addr = (unsigned char *)hw_addr; + unsigned short xsum = 0; + struct net_local *lp; + + printk("%s: EtherExpress 16 at %#x ",dev->name,ioaddr); - printk("%s: EtherExpress at %#x, ",dev->name,ioaddr); + outb(ASIC_RST, ioaddr+EEPROM_Ctrl); + outb(0, ioaddr+EEPROM_Ctrl); + udelay(500); + outb(i586_RST, ioaddr+EEPROM_Ctrl); hw_addr[0] = eexp_hw_readeeprom(ioaddr,2); hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); - if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) + if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000)) { - printk("rejected: invalid address %04x%04x%04x\n", + printk(" rejected: invalid address %04x%04x%04x\n", hw_addr[2],hw_addr[1],hw_addr[0]); return ENODEV; } + /* Calculate the EEPROM checksum. Carry on anyway if it's bad, + * though. + */ + for (i = 0; i < 64; i++) + xsum += eexp_hw_readeeprom(ioaddr, i); + if (xsum != 0xbaba) + printk(" (bad EEPROM xsum 0x%02x)", xsum); + dev->base_addr = ioaddr; - for ( i=0 ; i<6 ; i++ ) - dev->dev_addr[i] = chw_addr[5-i]; + for ( i=0 ; i<6 ; i++ ) + dev->dev_addr[i] = ((unsigned char *)hw_addr)[5-i]; { - char irqmap[]={0, 9, 3, 4, 5, 10, 11, 0}; - char *ifmap[]={"AUI", "BNC", "10baseT"}; - enum iftype {AUI=0, BNC=1, TP=2}; + static char irqmap[]={0, 9, 3, 4, 5, 10, 11, 0}; unsigned short setupval = eexp_hw_readeeprom(ioaddr,0); - dev->irq = irqmap[setupval>>13]; - dev->if_port = !(setupval & 0x1000) ? AUI : - eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TP : BNC; - - printk("IRQ %d, Interface %s, ",dev->irq,ifmap[dev->if_port]); +#ifdef CONFIG_MCA + if (!MCA_bus) { /* for MCA, we have them already */ +#endif + /* Use the IRQ from EEPROM if none was given */ + if (!dev->irq) + dev->irq = irqmap[setupval>>13]; - outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); - outb(0,ioaddr+SET_IRQ); + dev->if_port = !(setupval & 0x1000) ? AUI : + eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TPE : BNC; +#ifdef CONFIG_MCA + } +#endif + buswidth = !((setupval & 0x400) >> 10); } - dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); - if (!dev->priv) - return -ENOMEM; + dev->priv = lp = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (!dev->priv) + return ENOMEM; memset(dev->priv, 0, sizeof(struct net_local)); - eexp_hw_ASICrst(dev); + printk("(IRQ %d, %s connector, %d-bit bus", dev->irq, + eexp_ifmap[dev->if_port], buswidth?8:16); + + eexp_hw_set_interface(dev); + + /* Find out how much RAM we have on the card */ + outw(0, dev->base_addr + WRITE_PTR); + for (i = 0; i < 32768; i++) + outw(0, dev->base_addr + DATAPORT); + + for (memory_size = 0; memory_size < 64; memory_size++) + { + outw(memory_size<<10, dev->base_addr + READ_PTR); + if (inw(dev->base_addr+DATAPORT)) + break; + outw(memory_size<<10, dev->base_addr + WRITE_PTR); + outw(memory_size | 0x5000, dev->base_addr+DATAPORT); + outw(memory_size<<10, dev->base_addr + READ_PTR); + if (inw(dev->base_addr+DATAPORT) != (memory_size | 0x5000)) + break; + } - { - unsigned short i586mso = 0x023e; - unsigned short old_wp,old_rp,old_a0,old_a1; - unsigned short a0_0,a1_0,a0_1,a1_1; - - old_wp = inw(ioaddr+WRITE_PTR); - old_rp = inw(ioaddr+READ_PTR); - outw(0x8000+i586mso,ioaddr+READ_PTR); - old_a1 = inw(ioaddr); - outw(i586mso,ioaddr+READ_PTR); - old_a0 = inw(ioaddr); - outw(i586mso,ioaddr+WRITE_PTR); - outw(0x55aa,ioaddr); - outw(i586mso,ioaddr+READ_PTR); - a0_0 = inw(ioaddr); - outw(0x8000+i586mso,ioaddr+WRITE_PTR); - outw(0x5a5a,ioaddr); - outw(0x8000+i586mso,ioaddr+READ_PTR); - a1_0 = inw(ioaddr); - outw(i586mso,ioaddr+READ_PTR); - a0_1 = inw(ioaddr); - outw(i586mso,ioaddr+WRITE_PTR); - outw(0x1234,ioaddr); - outw(0x8000+i586mso,ioaddr+READ_PTR); - a1_1 = inw(ioaddr); + /* Sort out the number of buffers. We may have 16, 32, 48 or 64k + * of RAM to play with. + */ + lp->num_tx_bufs = 4; + lp->rx_buf_end = 0x3ff6; + switch (memory_size) + { + case 64: + lp->rx_buf_end += 0x4000; + case 48: + lp->num_tx_bufs += 4; + lp->rx_buf_end += 0x4000; + case 32: + lp->rx_buf_end += 0x4000; + case 16: + printk(", %dk RAM)\n", memory_size); + break; + default: + printk(") bad memory size (%dk).\n", memory_size); + kfree(dev->priv); + return ENODEV; + break; + } - if ((a0_0 != a0_1) || (a1_0 != a1_1) || - (a1_0 != 0x5a5a) || (a0_0 != 0x55aa)) - { - printk("32k\n"); - PRIV(dev)->rx_buf_end = 0x7ff6; - PRIV(dev)->num_tx_bufs = 4; - } - else - { - printk("64k\n"); - PRIV(dev)->num_tx_bufs = 8; - PRIV(dev)->rx_buf_start = TX_BUF_START + (PRIV(dev)->num_tx_bufs*TX_BUF_SIZE); - PRIV(dev)->rx_buf_end = 0xfff6; - } + lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE); + lp->width = buswidth; - outw(0x8000+i586mso,ioaddr+WRITE_PTR); - outw(old_a1,ioaddr); - outw(i586mso,ioaddr+WRITE_PTR); - outw(old_a0,ioaddr); - outw(old_wp,ioaddr+WRITE_PTR); - outw(old_rp,ioaddr+READ_PTR); - } - - if (net_debug) - printk(version); dev->open = eexp_open; dev->stop = eexp_close; dev->hard_start_xmit = eexp_xmit; @@ -797,20 +1121,22 @@ } /* - * Read a word from eeprom location (0-63?) + * Read a word from the EtherExpress on-board serial EEPROM. + * The EEPROM contains 64 words of 16 bits. */ -static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location) +__initfunc(static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, + unsigned char location)) { unsigned short cmd = 0x180|(location&0x7f); unsigned short rval = 0,wval = EC_CS|i586_RST; int i; - + outb(EC_CS|i586_RST,ioaddr+EEPROM_Ctrl); - for ( i=0x100 ; i ; i>>=1 ) + for (i=0x100 ; i ; i>>=1 ) { - if (cmd&i) + if (cmd&i) wval |= EC_Wr; - else + else wval &= ~EC_Wr; outb(wval,ioaddr+EEPROM_Ctrl); @@ -818,14 +1144,14 @@ eeprom_delay(); outb(wval,ioaddr+EEPROM_Ctrl); eeprom_delay(); - } + } wval &= ~EC_Wr; outb(wval,ioaddr+EEPROM_Ctrl); - for ( i=0x8000 ; i ; i>>=1 ) + for (i=0x8000 ; i ; i>>=1 ) { outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); eeprom_delay(); - if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd) + if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd) rval |= i; outb(wval,ioaddr+EEPROM_Ctrl); eeprom_delay(); @@ -851,43 +1177,55 @@ static unsigned short eexp_hw_lasttxstat(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; - unsigned short old_rp = inw(ioaddr+READ_PTR); - unsigned short old_wp = inw(ioaddr+WRITE_PTR); unsigned short tx_block = lp->tx_reap; unsigned short status; - - if (!test_bit(0,(void *)&dev->tbusy) && lp->tx_head==lp->tx_reap) + + if ((!dev->tbusy) && lp->tx_head==lp->tx_reap) return 0x0000; do { - outw(tx_block,ioaddr+READ_PTR); - status = inw(ioaddr); - if (!Stat_Done(status)) + outw(tx_block & ~31, dev->base_addr + SM_PTR); + status = inw(dev->base_addr + SHADOW(tx_block)); + if (!Stat_Done(status)) { lp->tx_link = tx_block; - outw(old_rp,ioaddr+READ_PTR); - outw(old_wp,ioaddr+WRITE_PTR); return status; } - else + else { lp->last_tx_restart = 0; lp->stats.collisions += Stat_NoColl(status); - if (!Stat_OK(status)) + if (!Stat_OK(status)) { - if (Stat_Abort(status)) - lp->stats.tx_aborted_errors++; - if (Stat_TNoCar(status) || Stat_TNoCTS(status)) + char *whatsup = NULL; + lp->stats.tx_errors++; + if (Stat_Abort(status)) + lp->stats.tx_aborted_errors++; + if (Stat_TNoCar(status)) { + whatsup = "aborted, no carrier"; lp->stats.tx_carrier_errors++; - if (Stat_TNoDMA(status)) - lp->stats.tx_fifo_errors++; + } + if (Stat_TNoCTS(status)) { + whatsup = "aborted, lost CTS"; + lp->stats.tx_carrier_errors++; + } + if (Stat_TNoDMA(status)) { + whatsup = "FIFO underran"; + lp->stats.tx_fifo_errors++; + } + if (Stat_TXColl(status)) { + whatsup = "aborted, too many collisions"; + lp->stats.tx_aborted_errors++; + } + if (whatsup) + printk(KERN_INFO "%s: transmit %s\n", + dev->name, whatsup); } else lp->stats.tx_packets++; } - if (tx_block == TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) + if (tx_block == TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE)) lp->tx_reap = tx_block = TX_BUF_START; else lp->tx_reap = tx_block += TX_BUF_SIZE; @@ -897,43 +1235,37 @@ while (lp->tx_reap != lp->tx_head); lp->tx_link = lp->tx_tail + 0x08; - outw(old_rp,ioaddr+READ_PTR); - outw(old_wp,ioaddr+WRITE_PTR); return status; } -/* - * This should never happen. It is called when some higher - * routine detects the CU has stopped, to try to restart - * it from the last packet we knew we were working on, - * or the idle loop if we had finished for the time. +/* + * This should never happen. It is called when some higher routine detects + * that the CU has stopped, to try to restart it from the last packet we knew + * we were working on, or the idle loop if we had finished for the time. */ static void eexp_hw_txrestart(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; unsigned short ioaddr = dev->base_addr; - + lp->last_tx_restart = lp->tx_link; - outw(lp->tx_link,ioaddr+SCB_CBL); - outw(SCB_CUstart,ioaddr+SCB_CMD); - outw(0,ioaddr+SCB_STATUS); + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); { unsigned short boguscount=50,failcount=5; - while (!inw(ioaddr+SCB_STATUS)) + while (!scb_status(dev)) { - if (!--boguscount) + if (!--boguscount) { - if (--failcount) + if (--failcount) { - printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", - dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD)); - outw(lp->tx_link,ioaddr+SCB_CBL); - outw(0,ioaddr+SCB_STATUS); - outw(SCB_CUstart,ioaddr+SCB_CMD); + printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", dev->name, scb_status(dev), scb_rdcmd(dev)); + scb_wrcbl(dev, lp->tx_link); + scb_command(dev, SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); boguscount = 100; } @@ -951,40 +1283,39 @@ } /* - * Writes down the list of transmit buffers into card - * memory. Initial separate, repeated transmits link - * them into a circular list, such that the CU can - * be constantly active, and unlink them as we reap - * transmitted packet buffers, so the CU doesn't loop - * and endlessly transmit packets. (Try hacking the driver - * to send continuous broadcast messages, say ARP requests - * on a subnet with Windows boxes running on Novell and - * LAN Workplace with EMM386. Amusing to watch them all die - * horribly leaving the Linux boxes up!) + * Writes down the list of transmit buffers into card memory. Each + * entry consists of an 82586 transmit command, followed by a jump + * pointing to itself. When we want to transmit a packet, we write + * the data into the appropriate transmit buffer and then modify the + * preceding jump to point at the new transmit command. This means that + * the 586 command unit is continuously active. */ static void eexp_hw_txinit(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; - unsigned short old_wp = inw(ioaddr+WRITE_PTR); unsigned short tx_block = TX_BUF_START; unsigned short curtbuf; + unsigned short ioaddr = dev->base_addr; - for ( curtbuf=0 ; curtbufnum_tx_bufs ; curtbuf++ ) + for ( curtbuf=0 ; curtbufnum_tx_bufs ; curtbuf++ ) { - outw(tx_block,ioaddr+WRITE_PTR); - outw(0x0000,ioaddr); - outw(Cmd_INT|Cmd_Xmit,ioaddr); - outw(tx_block+0x08,ioaddr); - outw(tx_block+0x0e,ioaddr); - outw(0x0000,ioaddr); - outw(0x0000,ioaddr); - outw(tx_block+0x08,ioaddr); - outw(0x8000,ioaddr); - outw(-1,ioaddr); - outw(tx_block+0x16,ioaddr); - outw(0x0000,ioaddr); + outw(tx_block, ioaddr + WRITE_PTR); + + outw(0x0000, ioaddr + DATAPORT); + outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT); + outw(tx_block+0x08, ioaddr + DATAPORT); + outw(tx_block+0x0e, ioaddr + DATAPORT); + + outw(0x0000, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + outw(tx_block+0x08, ioaddr + DATAPORT); + + outw(0x8000, ioaddr + DATAPORT); + outw(-1, ioaddr + DATAPORT); + outw(tx_block+0x16, ioaddr + DATAPORT); + outw(0x0000, ioaddr + DATAPORT); + tx_block += TX_BUF_SIZE; } lp->tx_head = TX_BUF_START; @@ -992,119 +1323,151 @@ lp->tx_tail = tx_block - TX_BUF_SIZE; lp->tx_link = lp->tx_tail + 0x08; lp->rx_buf_start = tx_block; - outw(old_wp,ioaddr+WRITE_PTR); -} -/* is this a standard test pattern, or dbecker randomness? */ - -unsigned short rx_words[] = -{ - 0xfeed,0xf00d,0xf001,0x0505,0x2424,0x6565,0xdeaf -}; +} /* - * Write the circular list of receive buffer descriptors to - * card memory. Note, we no longer mark the end of the list, - * so if all the buffers fill up, the 82586 will loop until - * we free one. This may sound dodgy, but it works, and - * it makes the error detection in the interrupt handler - * a lot simpler. + * Write the circular list of receive buffer descriptors to card memory. + * The end of the list isn't marked, which means that the 82586 receive + * unit will loop until buffers become available (this avoids it giving us + * "out of resources" messages). */ static void eexp_hw_rxinit(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; - unsigned short old_wp = inw(ioaddr+WRITE_PTR); unsigned short rx_block = lp->rx_buf_start; + unsigned short ioaddr = dev->base_addr; lp->num_rx_bufs = 0; - lp->rx_first = rx_block; - do + lp->rx_first = lp->rx_ptr = rx_block; + do { lp->num_rx_bufs++; - outw(rx_block,ioaddr+WRITE_PTR); - outw(0x0000,ioaddr); - outw(0x0000,ioaddr); - outw(rx_block+RX_BUF_SIZE,ioaddr); - outw(rx_block+0x16,ioaddr); - outsw(ioaddr, rx_words, sizeof(rx_words)>>1); - outw(0x8000,ioaddr); - outw(-1,ioaddr); - outw(rx_block+0x20,ioaddr); - outw(0x0000,ioaddr); - outw(0x8000|(RX_BUF_SIZE-0x20),ioaddr); + + outw(rx_block, ioaddr + WRITE_PTR); + + outw(0, ioaddr + DATAPORT); outw(0, ioaddr+DATAPORT); + outw(rx_block + RX_BUF_SIZE, ioaddr+DATAPORT); + outw(0xffff, ioaddr+DATAPORT); + + outw(0x0000, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + outw(0xdead, ioaddr+DATAPORT); + + outw(0x0000, ioaddr+DATAPORT); + outw(rx_block + RX_BUF_SIZE + 0x16, ioaddr+DATAPORT); + outw(rx_block + 0x20, ioaddr+DATAPORT); + outw(0, ioaddr+DATAPORT); + outw(RX_BUF_SIZE-0x20, ioaddr+DATAPORT); + lp->rx_last = rx_block; rx_block += RX_BUF_SIZE; } while (rx_block <= lp->rx_buf_end-RX_BUF_SIZE); - outw(lp->rx_last+4,ioaddr+WRITE_PTR); - outw(lp->rx_first,ioaddr); - outw(old_wp,ioaddr+WRITE_PTR); + /* Make first Rx frame descriptor point to first Rx buffer + descriptor */ + outw(lp->rx_first + 6, ioaddr+WRITE_PTR); + outw(lp->rx_first + 0x16, ioaddr+DATAPORT); + + /* Close Rx frame descriptor ring */ + outw(lp->rx_last + 4, ioaddr+WRITE_PTR); + outw(lp->rx_first, ioaddr+DATAPORT); + + /* Close Rx buffer descriptor ring */ + outw(lp->rx_last + 0x16 + 2, ioaddr+WRITE_PTR); + outw(lp->rx_first + 0x16, ioaddr+DATAPORT); + } /* - * Reset the 586, fill memory (including calls to - * eexp_hw_[(rx)(tx)]init()) unreset, and start - * the configuration sequence. We don't wait for this - * to finish, but allow the interrupt handler to start - * the CU and RU for us. We can't start the receive/ - * transmission system up before we know that the - * hardware is configured correctly + * Un-reset the 586, and start the configuration sequence. We don't wait for + * this to finish, but allow the interrupt handler to start the CU and RU for + * us. We can't start the receive/transmission system up before we know that + * the hardware is configured correctly. */ + static void eexp_hw_init586(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; unsigned short ioaddr = dev->base_addr; + int i; #if NET_DEBUG > 6 - printk("%s: eexp_hw_init586()\n", dev->name); + printk("%s: eexp_hw_init586()\n", dev->name); #endif lp->started = 0; - set_loopback; + + set_loopback(dev); outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); - outb_p(i586_RST,ioaddr+EEPROM_Ctrl); - udelay(2000); /* delay 20ms */ - { - unsigned long ofs; - for (ofs = 0; ofs < lp->rx_buf_end; ofs += 32) { - unsigned long i; - outw_p(ofs, ioaddr+SM_PTR); - for (i = 0; i < 16; i++) { - outw_p(0, ioaddr+SM_ADDR(i<<1)); - } - } - } - outw_p(lp->rx_buf_end,ioaddr+WRITE_PTR); - start_code[28] = (dev->flags & IFF_PROMISC)?(start_code[28] | 1):(start_code[28] & ~1); - lp->promisc = dev->flags & IFF_PROMISC; - /* We may die here */ - outsw(ioaddr, start_code, sizeof(start_code)>>1); - outw(CONF_HW_ADDR,ioaddr+WRITE_PTR); - outsw(ioaddr,dev->dev_addr,3); + /* Download the startup code */ + outw(lp->rx_buf_end & ~31, ioaddr + SM_PTR); + outw(lp->width?0x0001:0x0000, ioaddr + 0x8006); + outw(0x0000, ioaddr + 0x8008); + outw(0x0000, ioaddr + 0x800a); + outw(0x0000, ioaddr + 0x800c); + outw(0x0000, ioaddr + 0x800e); + + for (i = 0; i < (sizeof(start_code)); i+=32) { + int j; + outw(i, ioaddr + SM_PTR); + for (j = 0; j < 16; j+=2) + outw(start_code[(i+j)/2], + ioaddr+0x4000+j); + for (j = 0; j < 16; j+=2) + outw(start_code[(i+j+16)/2], + ioaddr+0x8000+j); + } + + /* Do we want promiscuous mode or multicast? */ + outw(CONF_PROMISC & ~31, ioaddr+SM_PTR); + i = inw(ioaddr+SHADOW(CONF_PROMISC)); + outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1), + ioaddr+SHADOW(CONF_PROMISC)); + lp->was_promisc = dev->flags & IFF_PROMISC; +#if 0 + eexp_setup_filter(dev); +#endif + + /* Write our hardware address */ + outw(CONF_HWADDR & ~31, ioaddr+SM_PTR); + outw(((unsigned short *)dev->dev_addr)[0], ioaddr+SHADOW(CONF_HWADDR)); + outw(((unsigned short *)dev->dev_addr)[1], + ioaddr+SHADOW(CONF_HWADDR+2)); + outw(((unsigned short *)dev->dev_addr)[2], + ioaddr+SHADOW(CONF_HWADDR+4)); + eexp_hw_txinit(dev); eexp_hw_rxinit(dev); - outw(0,ioaddr+WRITE_PTR); - outw(1,ioaddr); + outb(0,ioaddr+EEPROM_Ctrl); - outw(0,ioaddr+SCB_CMD); + udelay(5000); + + scb_command(dev, 0xf000); outb(0,ioaddr+SIGNAL_CA); + + outw(0, ioaddr+SM_PTR); + { unsigned short rboguscount=50,rfailcount=5; - while (outw(0,ioaddr+READ_PTR),inw(ioaddr)) + while (inw(ioaddr+0x4000)) { - if (!--rboguscount) + if (!--rboguscount) { printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n", dev->name); - outw(0,ioaddr+SCB_CMD); + scb_command(dev, 0); outb(0,ioaddr+SIGNAL_CA); rboguscount = 100; - if (!--rfailcount) + if (!--rfailcount) { printk(KERN_WARNING "%s: i82586 not responding, giving up.\n", dev->name); @@ -1114,27 +1477,26 @@ } } - outw(CONF_LINK,ioaddr+SCB_CBL); - outw(0,ioaddr+SCB_STATUS); - outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD); + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, 0xf000|SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); + { unsigned short iboguscount=50,ifailcount=5; - while (!inw(ioaddr+SCB_STATUS)) + while (!scb_status(dev)) { - if (!--iboguscount) + if (!--iboguscount) { - if (--ifailcount) + if (--ifailcount) { printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x, cmd %04x\n", - dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD)); - outw(CONF_LINK,ioaddr+SCB_CBL); - outw(0,ioaddr+SCB_STATUS); - outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD); + dev->name, scb_status(dev), scb_rdcmd(dev)); + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, 0xf000|SCB_CUstart); outb(0,ioaddr+SIGNAL_CA); iboguscount = 100; } - else + else { printk(KERN_WARNING "%s: Failed to initialize i82586, giving up.\n",dev->name); return; @@ -1142,9 +1504,10 @@ } } } - + + clear_loopback(dev); outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); - clear_loopback; + lp->init_time = jiffies; #if NET_DEBUG > 6 printk("%s: leaving eexp_hw_init586()\n", dev->name); @@ -1152,63 +1515,88 @@ return; } -/* - * completely reset the EtherExpress hardware. We will most likely get - * an interrupt during this whether we want one or not. It is best, - * therefore, to call this while we don't have a request_irq() on. - */ - -static void eexp_hw_ASICrst(struct device *dev) +static void eexp_setup_filter(struct device *dev) { + struct dev_mc_list *dmi = dev->mc_list; unsigned short ioaddr = dev->base_addr; - unsigned short wrval = 0x0001,succount=0,boguscount=500; - - outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); - - PRIV(dev)->started = 0; - outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl); - while (succount<20) - { - if (wrval == 0xffff) - wrval = 0x0001; - outw(0,ioaddr+WRITE_PTR); - outw(wrval,ioaddr); - outw(0,ioaddr+READ_PTR); - if (wrval++ == inw(ioaddr)) - succount++; - else - { - succount = 0; - if (!boguscount--) - { - boguscount = 500; - printk("%s: Having problems resetting EtherExpress ASIC, continuing...\n", - dev->name); - wrval = 0x0001; - outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl); - } + int count = dev->mc_count; + int i; + if (count > 8) { + printk(KERN_INFO "%s: too many multicast addresses (%d)\n", + dev->name, count); + count = 8; + } + + outw(CONF_NR_MULTICAST & ~31, ioaddr+SM_PTR); + outw(count, ioaddr+SHADOW(CONF_NR_MULTICAST)); + for (i = 0; i < count; i++) { + unsigned short *data = (unsigned short *)dmi->dmi_addr; + if (!dmi) { + printk(KERN_INFO "%s: too few multicast addresses\n", dev->name); + break; } + if (dmi->dmi_addrlen != ETH_ALEN) { + printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name); + continue; + } + outw((CONF_MULTICAST+(6*i)) & ~31, ioaddr+SM_PTR); + outw(data[0], ioaddr+SHADOW(CONF_MULTICAST+(6*i))); + outw((CONF_MULTICAST+(6*i)+2) & ~31, ioaddr+SM_PTR); + outw(data[1], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+2)); + outw((CONF_MULTICAST+(6*i)+4) & ~31, ioaddr+SM_PTR); + outw(data[2], ioaddr+SHADOW(CONF_MULTICAST+(6*i)+4)); } - outb(i586_RST,ioaddr+EEPROM_Ctrl); } - /* * Set or clear the multicast filter for this adaptor. - * We have to do a complete 586 restart for this to take effect. - * At the moment only promiscuous mode is supported. */ static void eexp_set_multicast(struct device *dev) { - if ((dev->flags & IFF_PROMISC) != PRIV(dev)->promisc) - eexp_hw_init586(dev); + unsigned short ioaddr = dev->base_addr; + struct net_local *lp = (struct net_local *)dev->priv; + int kick = 0, i; + if ((dev->flags & IFF_PROMISC) != lp->was_promisc) { + outw(CONF_PROMISC & ~31, ioaddr+SM_PTR); + i = inw(ioaddr+SHADOW(CONF_PROMISC)); + outw((dev->flags & IFF_PROMISC)?(i|1):(i & ~1), + ioaddr+SHADOW(CONF_PROMISC)); + lp->was_promisc = dev->flags & IFF_PROMISC; + kick = 1; + } + if (!(dev->flags & IFF_PROMISC)) { + eexp_setup_filter(dev); + if (lp->old_mc_count != dev->mc_count) { + kick = 1; + lp->old_mc_count = dev->mc_count; + } + } + if (kick) { + unsigned long oj; + scb_command(dev, SCB_CUsuspend); + outb(0, ioaddr+SIGNAL_CA); + outb(0, ioaddr+SIGNAL_CA); +#if 0 + printk("%s: waiting for CU to go suspended\n", dev->name); +#endif + oj = jiffies; + while ((SCB_CUstat(scb_status(dev)) == 2) && + ((jiffies-oj) < 2000)); + if (SCB_CUstat(scb_status(dev)) == 2) + printk("%s: warning, CU didn't stop\n", dev->name); + lp->started &= ~(STARTED_CU); + scb_wrcbl(dev, CONF_LINK); + scb_command(dev, SCB_CUstart); + outb(0, ioaddr+SIGNAL_CA); + } } /* * MODULE stuff */ + #ifdef MODULE #define EEXP_MAX_CARDS 4 /* max number of cards to support */ @@ -1216,14 +1604,19 @@ static char namelist[NAMELEN * EEXP_MAX_CARDS] = { 0, }; -static struct device dev_eexp[EEXP_MAX_CARDS] = +static struct device dev_eexp[EEXP_MAX_CARDS] = { { NULL, /* will allocate dynamically */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe }, + 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe }, }; -int irq[EEXP_MAX_CARDS] = {0, }; -int io[EEXP_MAX_CARDS] = {0, }; +static int irq[EEXP_MAX_CARDS] = {0, }; +static int io[EEXP_MAX_CARDS] = {0, }; + +/* +MODULE_PARM(io, "1-" __MODULE_STRING(EEXP_MAX_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(EEXP_MAX_CARDS) "i"); +*/ /* Ideally the user would give us io=, irq= for every card. If any parameters * are specified, we verify and then use them. If no parameters are given, we @@ -1238,10 +1631,12 @@ dev->name = namelist + (NAMELEN*this_dev); dev->irq = irq[this_dev]; dev->base_addr = io[this_dev]; +#ifndef CONFIG_MCA if (io[this_dev] == 0) { if (this_dev) break; printk(KERN_NOTICE "eexpress.c: Module autoprobe not recommended, give io=xx.\n"); } +#endif if (register_netdev(dev) != 0) { printk(KERN_WARNING "eexpress.c: Failed to register card at 0x%x.\n", io[this_dev]); if (found != 0) return 0; @@ -1255,7 +1650,7 @@ void cleanup_module(void) { int this_dev; - + for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) { struct device *dev = &dev_eexp[this_dev]; if (dev->priv != NULL) { @@ -1272,6 +1667,5 @@ * Local Variables: * c-file-style: "linux" * tab-width: 8 - * compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -c 3c505.c" * End: */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/eexpress.h linux-2.0.35-mca/drivers/net/eexpress.h --- linux/drivers/net/eexpress.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/eexpress.h Sat Aug 8 22:20:48 1998 @@ -0,0 +1,179 @@ +/* + * eexpress.h: Intel EtherExpress16 defines + */ + +/* + * EtherExpress card register addresses + * as offsets from the base IO region (dev->base_addr) + */ + +#define DATAPORT 0x0000 +#define WRITE_PTR 0x0002 +#define READ_PTR 0x0004 +#define SIGNAL_CA 0x0006 +#define SET_IRQ 0x0007 +#define SM_PTR 0x0008 +#define MEM_Dec 0x000a +#define MEM_Ctrl 0x000b +#define MEM_Page_Ctrl 0x000c +#define Config 0x000d +#define EEPROM_Ctrl 0x000e +#define ID_PORT 0x000f +#define MEM_ECtrl 0x000f + +/* + * card register defines + */ + +/* SET_IRQ */ +#define SIRQ_en 0x08 +#define SIRQ_dis 0x00 + +/* EEPROM_Ctrl */ +#define EC_Clk 0x01 +#define EC_CS 0x02 +#define EC_Wr 0x04 +#define EC_Rd 0x08 +#define ASIC_RST 0x40 +#define i586_RST 0x80 + +#define eeprom_delay() { udelay(40); } + +/* + * i82586 Memory Configuration + */ + +/* (System Configuration Pointer) System start up block, read after 586_RST */ +#define SCP_START 0xfff6 + +/* Intermediate System Configuration Pointer */ +#define ISCP_START 0x0000 + +/* System Command Block */ +#define SCB_START 0x0008 + +/* Start of buffer region. Everything before this is used for control + * structures and the CU configuration program. The memory layout is + * determined in eexp_hw_probe(), once we know how much memory is + * available on the card. + */ + +#define TX_BUF_START 0x0100 + +#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f) +#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f) + +/* + * SCB defines + */ + +/* these functions take the SCB status word and test the relevant status bit */ +#define SCB_complete(s) ((s&0x8000)!=0) +#define SCB_rxdframe(s) ((s&0x4000)!=0) +#define SCB_CUdead(s) ((s&0x2000)!=0) +#define SCB_RUdead(s) ((s&0x1000)!=0) +#define SCB_ack(s) (s & 0xf000) + +/* Command unit status: 0=idle, 1=suspended, 2=active */ +#define SCB_CUstat(s) ((s&0x0300)>>8) + +/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */ +#define SCB_RUstat(s) ((s&0x0070)>>4) + +/* SCB commands */ +#define SCB_CUnop 0x0000 +#define SCB_CUstart 0x0100 +#define SCB_CUresume 0x0200 +#define SCB_CUsuspend 0x0300 +#define SCB_CUabort 0x0400 +#define SCB_resetchip 0x0080 + +#define SCB_RUnop 0x0000 +#define SCB_RUstart 0x0010 +#define SCB_RUresume 0x0020 +#define SCB_RUsuspend 0x0030 +#define SCB_RUabort 0x0040 + +/* + * Command block defines + */ + +#define Stat_Done(s) ((s&0x8000)!=0) +#define Stat_Busy(s) ((s&0x4000)!=0) +#define Stat_OK(s) ((s&0x2000)!=0) +#define Stat_Abort(s) ((s&0x1000)!=0) +#define Stat_STFail ((s&0x0800)!=0) +#define Stat_TNoCar(s) ((s&0x0400)!=0) +#define Stat_TNoCTS(s) ((s&0x0200)!=0) +#define Stat_TNoDMA(s) ((s&0x0100)!=0) +#define Stat_TDefer(s) ((s&0x0080)!=0) +#define Stat_TColl(s) ((s&0x0040)!=0) +#define Stat_TXColl(s) ((s&0x0020)!=0) +#define Stat_NoColl(s) (s&0x000f) + +/* Cmd_END will end AFTER the command if this is the first + * command block after an SCB_CUstart, but BEFORE the command + * for all subsequent commands. Best strategy is to place + * Cmd_INT on the last command in the sequence, followed by a + * dummy Cmd_Nop with Cmd_END after this. + */ + +#define Cmd_END 0x8000 +#define Cmd_SUS 0x4000 +#define Cmd_INT 0x2000 + +#define Cmd_Nop 0x0000 +#define Cmd_SetAddr 0x0001 +#define Cmd_Config 0x0002 +#define Cmd_MCast 0x0003 +#define Cmd_Xmit 0x0004 +#define Cmd_TDR 0x0005 +#define Cmd_Dump 0x0006 +#define Cmd_Diag 0x0007 + + +/* + * Frame Descriptor (Receive block) defines + */ + +#define FD_Done(s) ((s&0x8000)!=0) +#define FD_Busy(s) ((s&0x4000)!=0) +#define FD_OK(s) ((s&0x2000)!=0) + +#define FD_CRC(s) ((s&0x0800)!=0) +#define FD_Align(s) ((s&0x0400)!=0) +#define FD_Resrc(s) ((s&0x0200)!=0) +#define FD_DMA(s) ((s&0x0100)!=0) +#define FD_Short(s) ((s&0x0080)!=0) +#define FD_NoEOF(s) ((s&0x0040)!=0) + +struct rfd_header { + volatile unsigned long flags; + volatile unsigned short link; + volatile unsigned short rbd_offset; + volatile unsigned short dstaddr1; + volatile unsigned short dstaddr2; + volatile unsigned short dstaddr3; + volatile unsigned short srcaddr1; + volatile unsigned short srcaddr2; + volatile unsigned short srcaddr3; + volatile unsigned short length; + + /* This is actually a Receive Buffer Descriptor. The way we + * arrange memory means that an RBD always follows the RFD that + * points to it, so they might as well be in the same structure. + */ + volatile unsigned short actual_count; + volatile unsigned short next_rbd; + volatile unsigned short buf_addr1; + volatile unsigned short buf_addr2; + volatile unsigned short size; +}; + +/* Returned data from the Time Domain Reflectometer */ + +#define TDR_LINKOK (1<<15) +#define TDR_XCVRPROBLEM (1<<14) +#define TDR_OPEN (1<<13) +#define TDR_SHORT (1<<12) +#define TDR_TIME 0x7ff diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/ne2.c linux-2.0.35-mca/drivers/net/ne2.c --- linux/drivers/net/ne2.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/ne2.c Sat Aug 8 22:20:48 1998 @@ -0,0 +1,658 @@ +/* ne2.c: A ne/2 ethernet driver for linux. */ +/* + Based on the NE2000 driver written by Donald Becker (1992-94). + modified by Wim Dumon (Apr 1996) + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached as wimpie@linux.cc.kuleuven.ac.be + + Currently suported : NE/2 + This patch was never tested on other MCA-ethernet adaptors, but it + might work. Just give it a try and let me know if you have problems. + Also mail me if it realy works, please ! + + Changelog : + Mon Feb 3 16:26:02 MET 1997 + - adapted the driver to work with the 2.1.25 kernel + - multiple ne2 support (untested) + - module support (untested) + +* WARNING + ------- + This is alpha-test software. It is not guaranteed to work. As a + matter of fact, I'm quite sure there are *LOTS* of bugs in here. I + would like to hear from you if you use this driver, even if it works. + If it doesn't work, be sure to send me a mail with the problems ! + +*/ + + +static const char *version = + "ne2.c:v0.02 Feb 3 1997 Wim Dumon +(Wim.Dumon@linux.cc.kuleuven.ac.be)\n"; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "8390.h" + +/* Some defines that people can play with if so inclined. */ + +/* Do we perform extra sanity checks on stuff ? */ +/* #define NE_SANITY_CHECK */ + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT 0x10 /* NatSemi-defined port window +offset. */ +#define NE_RESET 0x20 /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT 0x30 + +#define NE1SM_START_PG 0x20 /* First page of TX buffer */ +#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */ +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + +/*From the .ADF file :*/ +static unsigned int addresses[7]= + {0x1000, 0x2020, 0x8020, 0xa0a0, 0xb0b0, 0xc0c0, +0xc3d0}; +static int irqs[4]={3, 4, 5, 9}; + + +int ne2_probe(struct device *dev); +static int ne2_probe1(struct device *dev, int slot); + +static int ne_open(struct device *dev); +static int ne_close(struct device *dev); + +static void ne_reset_8390(struct device *dev); +static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne_block_input(struct device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne_block_output(struct device *dev, const int count, + const unsigned char *buf, const int start_page); + + + +/* + * Note that at boot, this probe only picks up one card at a time. + */ + +int ne2_probe(struct device *dev) +{ + static int current_mca_slot=-1; + + /* Do not check any supplied i/o locations. POS registers usualy don't + fail :) */ + +#ifndef MODULE + /* MCA cards have POS registers. Autodetecting MCA cards is extremely + simple. Just search for the card.*/ + current_mca_slot = mca_find_adapter(0x7154, ++current_mca_slot); + if (current_mca_slot!=MCA_NOTFOUND) { + mca_set_adapter_name(current_mca_slot, "NE/2 Ethernet card (ID +0x7154)"); + return ne2_probe1(dev, current_mca_slot); + } else + return ENODEV; +#endif + + return ENODEV; +} + +static int ne2_procinfo(char *buf, int slot, struct device *dev) +{ + int len=0; + + len += sprintf(buf+len, "The NE/2 ethernet adapter\n" ); + len += sprintf(buf+len, "Driver written by Wim Dumon +base_addr); + len += sprintf(buf+len, "IRQ : %d\n", dev->irq); +#define HW_ADDR(i) dev->dev_addr[i] + len += sprintf(buf+len, "HW addr : %x:%x:%x:%x:%x:%x\n", HW_ADDR(1), + HW_ADDR(2), HW_ADDR(3), HW_ADDR(4), HW_ADDR(5), +HW_ADDR(6)); +#undef HW_ADDR + + return len; +} + +static int ne2_probe1(struct device *dev, int slot) +{ + int i, base_addr, irq; + unsigned char POS; + unsigned char SA_prom[32]; + const char *name = "NE/2"; + int start_page, stop_page; + static unsigned version_printed = 0; + + + /* We should have a "dev" from Space.c or the static module table. */ + if (dev == NULL) { + printk(KERN_ERR "ne.c: Passed a NULL device.\n"); + dev = init_etherdev(0, 0); + } + + if (ei_debug && version_printed++ == 0) + printk(version); + + printk("NE/2 ethercard found in slot %d:", slot); + + /* Read base IO and IRQ from the POS-registers */ + POS=mca_read_stored_pos(slot, 2); + if(!(POS % 2)) { + printk(" disabled.\n"); + return ENODEV; + } + + i = (POS & 0xe)>>1; +/* printk("Halleluja sdog, als er na de pijl een 1 staat is 1 - 1 == 0" + " en zou het moeten werken -> %d\n", i); + The above line was for remote testing, thanx to sdog ... */ + base_addr = addresses[i - 1]; + irq = irqs[(POS & 0x60)>>5]; +#ifdef DEBUG + + printk("POS info : pos 2 = %#x ; base = %#x ; irq = %d\n", POS, + base_addr, irq); +#endif + +#ifndef CRYNWR_WAY + /*Reset the card the way they do it in the Crynwr packet driver*/ + for (i=0; i<8; i++) outb(0x0,base_addr + NE_RESET); + inb(base_addr + NE_RESET); + outb(0x21, base_addr + NE_CMD); + if (inb(base_addr + NE_CMD)!=0x21) { + printk("NE2 not responding\n"); + return ENODEV; + } + + /*In the crynwr sources they do a RAM-test here. I skip it. I suppose +my + RAM is okay. Suppose your memory is broken. Then this test should +fail + and you won't be able to use your card. But if I do not test, you +won't + be able to use your card, neither. So this test won't help you. + */ +#else /* _I_ never tested it this way .. Go ahead and try ...*/ + /* Reset card. Who knows what dain-bramaged state it was left in. */ + { unsigned long reset_start_time = jiffies; + + /* DON'T change these to inb_p/outb_p or reset will fail on +clones.. +*/ + outb(inb(base_addr + NE_RESET), base_addr + NE_RESET); + + while ((inb_p(base_addr + EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2*HZ/100) { + printk(" not found (no reset ack).\n"); + return ENODEV; + } + + outb_p(0xff, base_addr + EN0_ISR); /* Ack all intr. */ + } +#endif + + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, +0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select +page 0*/ + {0x49, EN0_DCFG}, /* Set WORD-wide (0x49) +access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) + outb_p(program_seq[i].value, base_addr + +program_seq[i].offset); + + } + for(i = 0; i < 6 /*sizeof(SA_prom)*/; i+=2) { + SA_prom[i] = inb(base_addr + NE_DATAPORT); + } + + start_page = NESM_START_PG; + stop_page = NESM_STOP_PG; + + dev->irq=irq; + + /* Snarf the interrupt now. There's no point in waiting since we +cannot + share and the board will usually be enabled. */ + { + int irqval = request_irq(dev->irq, ei_interrupt, 0, name, NULL); + if (irqval) { + printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, +irqval); + return EAGAIN; + } + } + + dev->base_addr = base_addr; + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (" unable to get memory for dev->priv.\n"); + free_irq(dev->irq, NULL); + return -ENOMEM; + } + + request_region(base_addr, NE_IO_EXTENT, name); + + for(i = 0; i < ETHER_ADDR_LEN; i++) { + printk(" %2.2x", SA_prom[i]); + dev->dev_addr[i] = SA_prom[i]; + } + + printk("\n%s: %s found at %#x, using IRQ %d.\n", + dev->name, name, base_addr, dev->irq); + + mca_set_adapter_procfn(slot, (MCA_ProcFn) ne2_procinfo, dev); + + ei_status.name = name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = (2 == 2); + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne_reset_8390; + ei_status.block_input = &ne_block_input; + ei_status.block_output = &ne_block_output; + ei_status.get_8390_hdr = &ne_get_8390_hdr; + dev->open = &ne_open; + dev->stop = &ne_close; + NS8390_init(dev, 0); + return 0; +} + +static int +ne_open(struct device *dev) +{ + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int +ne_close(struct device *dev) +{ + if (ei_debug > 1) + printk("%s: Shutting down ethercard.\n", dev->name); + ei_close(dev); + MOD_DEC_USE_COUNT; + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ +static void +ne_reset_8390(struct device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies); + + /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2*HZ/100) { + printk("%s: ne_reset_8390() did not complete.\n", dev->name); + break; + } + outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void +ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int +ring_page) +{ + + int nic_base = dev->base_addr; + + /* This *shouldn't* happen. If it does, it's the last thing you'll +see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_get_8390_hdr " + "[DMAstat:%d][irqlock:%d][intr:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb_p(0, nic_base + EN0_RCNTHI); + outb_p(0, nic_base + EN0_RSARLO); /* On page boundary +*/ + outb_p(ring_page, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.word16) + insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); + else + insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for +hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void +ne_block_input(struct device *dev, int count, struct sk_buff *skb, int +ring_offset) +{ +#ifdef NE_SANITY_CHECK + int xfer_count = count; +#endif + int nic_base = dev->base_addr; + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll +see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_input " + "[DMAstat:%d][irqlock:%d][intr:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + if (ei_status.word16) { + insw(NE_BASE + NE_DATAPORT,buf,count>>1); + if (count & 0x01) { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); +#ifdef NE_SANITY_CHECK + xfer_count++; +#endif + } + } else { + insb(NE_BASE + NE_DATAPORT, buf, count); + } + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. If you see + this message you either 1) have a slightly incompatible clone + or 2) have noise/speed problems with your bus. */ + if (ei_debug > 1) { /* DMA termination address check... */ + int addr, tries = 20; + do { + /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here + -- it's broken for Rx on some cards! */ + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if (((ring_offset + xfer_count) & 0xff) == low) + break; + } while (--tries > 0); + if (tries <= 0) + printk("%s: RX transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, ring_offset + xfer_count, addr); + } +#endif + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void +ne_block_output(struct device *dev, int count, + const unsigned char *buf, const int start_page) +{ + int nic_base = NE_BASE; + unsigned long dma_start; +#ifdef NE_SANITY_CHECK + int retries = 0; +#endif + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + if (ei_status.word16 && (count & 0x01)) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll +see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_output." + "[DMAstat:%d][irqlock:%d][intr:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE_SANITY_CHECK + retry: +#endif + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + outb_p(0x42, nic_base + EN0_RCNTLO); + outb_p(0x00, nic_base + EN0_RCNTHI); + outb_p(0x42, nic_base + EN0_RSARLO); + outb_p(0x00, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + /* Make certain that the dummy read has occurred. */ + SLOW_DOWN_IO; + SLOW_DOWN_IO; + SLOW_DOWN_IO; +#endif + + outb_p(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(0x00, nic_base + EN0_RSARLO); + outb_p(start_page, nic_base + EN0_RSARHI); + + outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD); + if (ei_status.word16) { + outsw(NE_BASE + NE_DATAPORT, buf, count>>1); + } else { + outsb(NE_BASE + NE_DATAPORT, buf, count); + } + + dma_start = jiffies; + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. */ + if (ei_debug > 1) { /* DMA termination address +check... */ + int addr, tries = 20; + do { + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if ((start_page << 8) + count == addr) + break; + } while (--tries > 0); + if (tries <= 0) { + printk("%s: Tx packet transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, (start_page << 8) + count, addr); + if (retries++ == 0) + goto retry; + } + } +#endif + + while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ + printk("%s: timeout waiting for Tx RDC.\n", dev->name); + ne_reset_8390(dev); + NS8390_init(dev,1); + break; + } + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + + +#ifdef MODULE +#define MAX_NE_CARDS 4 /* Max number of NE cards per module +*/ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, }; +static struct device dev_ne[MAX_NE_CARDS] = { + { + NULL, /* assign a chunk of namelist[] +below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_NE_CARDS] = { 0, }; +static int irq[MAX_NE_CARDS] = { 0, }; +static int bad[MAX_NE_CARDS] = { 0, }; /* 0xbad = bad sig or no +reset ack */ + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); + +/* This is set up so that no ISA autoprobe takes place. We can't guarantee +that the ne2k probe is the last 8390 based probe to take place (as it +is at boot) and so the probe will get confused by any other 8390 cards. +ISA device autoprobes on a running machine are not recommended anyway. */ + +int +init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct device *dev = &dev_ne[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->mem_end = bad[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = ne2_probe; + if (register_netdev(dev) == 0) { + found++; + continue; + } + if (found != 0) /* Got at least one. */ + return 0; + if (io[this_dev] != 0) + printk(KERN_WARNING "ne.c: No NE*000 card found +at i/o = %#x\n", +io[this_dev]); + else + printk(KERN_NOTICE "ne.c: No PCI cards found. Use +\"io=0xNNN\" value(s) for +ISA cards.\n"); + return -ENXIO; + } + + return 0; +} + +void +cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct device *dev = &dev_ne[this_dev]; + if (dev->priv != NULL) { + kfree(dev->priv); + dev->priv = NULL; + free_irq(dev->irq, NULL); + irq2dev_map[dev->irq] = NULL; + release_region(dev->base_addr, NE_IO_EXTENT); + unregister_netdev(dev); + } + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer +-I/usr/src/linux/net/tcp +-c ne2.c" + * version-control: t + * kept-new-versions: 5 + * End: + */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/smc-mca.c linux-2.0.35-mca/drivers/net/smc-mca.c --- linux/drivers/net/smc-mca.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/smc-mca.c Sat Aug 8 22:20:48 1998 @@ -0,0 +1,442 @@ +/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ +/* + Most of this driver, except for ultramca_probe is nearly + verbatim from smc-ultra.c by Donald Becker. The rest is + written and copyright 1996 by David Weis, weisd3458@uni.edu + + This is a driver for the SMC Ultra and SMC EtherEZ ethercards. + + This driver uses the cards in the 8390-compatible, shared memory mode. + Most of the run-time complexity is handled by the generic code in + 8390.c. The code in this file is responsible for + + This driver enables the shared memory only when doing the actual data + transfers to avoid a bug in early version of the card that corrupted + data transferred by a AHA1542. + + This driver does not support the programmed-I/O data transfer mode of + the EtherEZ. That support (if available) is smc-ez.c. Nor does it + use the non-8390-compatible "Altego" mode. (No support currently planned.) + + Changelog: + + Paul Gortmaker : multiple card support for module users. + David Weis : Micro Channel-ized it. + Tom Sightler : Added code for IBM PS/2 Ethernet Adapter/A probing + Christopher Turcksin : Changed MCA probe so that multiple adapters are + found correctly. (16/07/1997) (wabbit) + Chris Beauregard : tried to merge the above two changes (Dec 15, 1997) +*/ + + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "8390.h" +#include "smc-mca.h" +#include + +int ultramca_probe(struct device *dev); + +static int ultramca_open(struct device *dev); +static void ultramca_reset_8390(struct device *dev); +static void ultramca_get_8390_hdr(struct device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page); +static void ultramca_block_input(struct device *dev, int count, + struct sk_buff *skb, + int ring_offset); +static void ultramca_block_output(struct device *dev, int count, + const unsigned char *buf, + const start_page); +static int ultramca_close_card(struct device *dev); + +#define START_PG 0x00 /* First page of TX buffer */ + +#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */ +#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */ +#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */ +#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ +#define ULTRA_IO_EXTENT 32 +#define EN0_ERWCNT 0x08 /* Early receive warning count. */ + +/* wabbit - struct for a list of adapter names and IDs */ + +struct smc_mca_adapters_t { + unsigned int id; + char *name; + int is_ibm; /* actually more of a type indicator */ +}; + +/* wabbit - list of adapters supported by this driver */ + +const struct smc_mca_adapters_t smc_mca_adapters[] = { + { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)", 0 }, + { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)", 0 }, + { 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)", 2 }, + { 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)", 2 }, + { 0xefe5, "IBM PS/2 Adapter/A for Ethernet", 1 }, + { 0x0000, NULL } +}; + +#if 0 +__initfunc(int ultramca_probe(struct device *dev)) +#endif +int ultramca_probe(struct device *dev) +{ + unsigned short ioaddr; + unsigned char num_pages; + unsigned char reg4; + char slot = MCA_NOTFOUND; + unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff; + int i,j; + int adapter_found = 0; + int adapter = 0; + int tbase = 0, tirq = 0; + int base_addr = dev ? dev->base_addr : 0; + int irq = dev ? dev->irq : 0; + + if(!MCA_bus) { + return ENODEV; + } else { + if(base_addr || irq) { + printk( "Probing for SMC MCA adapter" ); + if(base_addr) { + printk(" at I/O address 0x%04x%c", base_addr, irq ? ' ' : '\n'); + } + if(irq) { + printk("using irq %d\n", irq); + } + } + } + + /* proper multicard detection by ZP Gu (zpg@castle.net) */ + for(j = 0; (smc_mca_adapters[j].name != NULL) && !adapter_found; j++) { + + slot = mca_find_unused_adapter(smc_mca_adapters[j].id, 0); + + while((slot != MCA_NOTFOUND) && !adapter_found) { + tirq = 0; + tbase = 0; + + /* If we're trying to match a specificied irq or + * io address, we'll reject the adapter + * found unless it's the one we're looking for + */ + + pos2 = mca_read_stored_pos(slot, 2); /* io_addr */ + pos3 = mca_read_stored_pos(slot, 3); /* shared mem */ + pos4 = mca_read_stored_pos(slot, 4); /* ROM bios address range */ + pos5 = mca_read_stored_pos(slot, 5); /* irq, media and RIPL */ + + /* Test the following conditions: + * If an irq parameter is supplied, compare it with the irq of the adapter we found + * If a base_addr paramater is given, compare it with the base_addr of the adapter we found + * Check that the irq and the base_addr of the adapter we found is not already in use by this driver + */ + + if(!smc_mca_adapters[adapter].is_ibm) { + tbase = addr_table[pos2 >> 4].base_addr; + tirq = irq_table[(pos5 & ~IRQ_MASK) >> 2].irq; + } else /* is_ibm == 1 or 2 */ { + tbase = ((pos2 & 254) * 0x10); + tirq = irq_table[(pos5 & 3)].ibm_irq; + } + + if( !tirq || !tbase || (irq && irq != tirq) + || (base_addr && tbase != base_addr ) + ) { + slot = mca_find_unused_adapter( + smc_mca_adapters[j].id, slot+1); + } else { + adapter_found = 1; + adapter = j; + } + } + } + + if(!adapter_found) { + return ((base_addr || irq) ? ENXIO : ENODEV); + } + + /* Adapter found. */ + + printk("%s: %s found in slot %d\n", + dev->name, smc_mca_adapters[adapter].name, slot+1); + + mca_set_adapter_name(slot, smc_mca_adapters[adapter].name); + + ioaddr = dev->base_addr = tbase; + dev->irq = tirq; + if( !smc_mca_adapters[adapter].is_ibm ) { + dev->mem_start = 0; + num_pages = 0x40; + for (i = 0; i < 15; i++) { + if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) { + dev->mem_start = mem_table[i].mem_start; + num_pages = mem_table[i].num_pages; + } + } + } else if( smc_mca_adapters[adapter].is_ibm == 2 ) { + /* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates + the index of the 0x2000 step. */ + dev->mem_start = 0xc0000 + 0x2000 * pos3; + num_pages = 0x20; + } else /* is_ibm == 1 */ { + dev->mem_start = ((pos3 & 252) * 0x1000); + num_pages = 0x40; + } + + if (dev->mem_start == 0) /* sanity check, shouldn't happen */ + return -ENODEV; + + reg4 = inb(ioaddr+4) & 0x7f; + outb(reg4, ioaddr+4); + + printk("%s: SMC Ultra MCA at %#3x,", dev->name, ioaddr); + + for (i = 0; i < 6; i++) { + printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr+8+i)); + } + + /* Switch from the station address to the alternate register set and + read the useful registers there. */ + outb(0x80 | reg4, ioaddr+4); + + /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ + outb(0x80 | inb(ioaddr+0x0c), ioaddr+0x0c); + + /* Switch back to the station address register set so that the MS-DOS driver + can find the card after a warm boot. */ + outb(reg4, ioaddr+4); + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk (", no memory for dev->priv.\n"); + return -ENOMEM; + } + + /* OK, we are certain this is going to work. Setup the device. */ + request_region(ioaddr, ULTRA_IO_EXTENT, "smc-mca"); + + /* The 8390 isn't at the base address, so fake the offset */ + dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; + + ei_status.name = "SMC Ultra MCA"; + ei_status.word16 = 1; + ei_status.tx_start_page = START_PG; + ei_status.rx_start_page = START_PG+TX_PAGES; + ei_status.stop_page = num_pages; + + dev->rmem_start = dev->mem_start+TX_PAGES*256; + dev->mem_end = dev->rmem_end = + dev->mem_start+(ei_status.stop_page - START_PG)*256; + + printk(", irq %d memory %#lx-%#lx.\n", dev->irq, dev->mem_start, dev->mem_end-1); + + ei_status.reset_8390 = &ultramca_reset_8390; + ei_status.block_input = &ultramca_block_input; + ei_status.block_output = &ultramca_block_output; + ei_status.get_8390_hdr = &ultramca_get_8390_hdr; + dev->open = &ultramca_open; + dev->stop = &ultramca_close_card; + NS8390_init(dev, 0); + + return 0; +} + +static int +ultramca_open(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + + if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL)) + return -EAGAIN; + + outb(ULTRA_MEMENB, ioaddr); /* Enable memory */ + outb(0x80, ioaddr+5); /* ??? */ + outb(0x01, ioaddr+6); /* Enable interrupts and memory. */ + outb(0x04, ioaddr+5); /* ??? */ + + /* Set the early receive warning level in window 0 high enough not + to receive ERW interrupts. */ + /* + outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); + outb(0xff, dev->base_addr+EN0_ERWCNT); + */ + + ei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static void +ultramca_reset_8390(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + + outb(ULTRA_RESET, ioaddr); + if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies); + ei_status.txing = 0; + + outb(0x80, ioaddr+5); /* ??? */ + outb(0x01, ioaddr+6); /* Enable interrupts and memory. */ + + if (ei_debug > 1) printk("reset done\n"); + return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void +ultramca_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + + unsigned long hdr_start = dev->mem_start+((ring_page - START_PG)<<8); + +#ifdef notdef + /* Officially this is what we are doing, but the readl() is faster */ + memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +#else + ((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +} + +/* Block input and output are easy on shared memory ethercards, the only + complication is when the ring buffer wraps. */ + +static void +ultramca_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + unsigned long xfer_start = dev->mem_start+ring_offset - (START_PG<<8); + + if (xfer_start+count > dev->rmem_end) { + /* We must wrap the input move. */ + int semi_count = dev->rmem_end - xfer_start; + memcpy_fromio(skb->data, xfer_start, semi_count); + count -= semi_count; + memcpy_fromio(skb->data+semi_count, dev->rmem_start, count); + } else { + /* Packet is in one chunk -- we can copy cksum. */ + eth_io_copy_and_sum(skb, xfer_start, count, 0); + } + +} + +static void +ultramca_block_output(struct device *dev, int count, const unsigned char *buf, + int start_page) +{ + unsigned long shmem = dev->mem_start+((start_page - START_PG)<<8); + + memcpy_toio(shmem, buf, count); + +} + +static int +ultramca_close_card(struct device *dev) +{ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + + dev->start = 0; + dev->tbusy = 1; + + if (ei_debug > 1) + printk("%s: Shutting down ethercard.\n", dev->name); + + outb(0x00, ioaddr + 6); /* Disable interrupts. */ + free_irq(dev->irq, NULL); + irq2dev_map[dev->irq] = 0; + + NS8390_init(dev, 0); + + /* We should someday disable shared memory and change to 8-bit mode + "just in case"... */ + + MOD_DEC_USE_COUNT; + + return 0; +} + + +#ifdef MODULE +#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */ +#define NAMELEN 8 /* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_ULTRAMCA_CARDS] = { 0, }; +static struct device dev_ultra[MAX_ULTRAMCA_CARDS] = { + { + NULL, /* assign a chunk of namelist[] below */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL + }, +}; + +static int io[MAX_ULTRAMCA_CARDS] = { 0, }; +static int irq[MAX_ULTRAMCA_CARDS] = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +/* !!! CHANGED: on MCA, we can reliably autoprobe */ +int +init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + dev->name = namelist+(NAMELEN*this_dev); + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->init = ultramca_probe; + + if (register_netdev(dev) != 0) { + if (found != 0) return 0; /* Got at least one. */ + printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found.\n"); + return -ENXIO; + } + found++; + } + + return 0; +} + +void +cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) { + struct device *dev = &dev_ultra[this_dev]; + if (dev->priv != NULL) { + /* NB: ultra_close_card() does free_irq irq2dev */ + int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; + kfree(dev->priv); + dev->priv = NULL; + release_region(ioaddr, ULTRA_IO_EXTENT); + unregister_netdev(dev); + } + } +} +#endif /* MODULE */ + + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-mca.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/net/smc-mca.h linux-2.0.35-mca/drivers/net/smc-mca.h --- linux/drivers/net/smc-mca.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/net/smc-mca.h Sat Aug 8 22:20:48 1998 @@ -0,0 +1,60 @@ +/* + djweis weisd3458@uni.edu + most of this file was taken from ps2esdi.h +*/ + +struct { + unsigned int base_addr; +} addr_table[] = { + { 0x0800 }, + { 0x1800 }, + { 0x2800 }, + { 0x3800 }, + { 0x4800 }, + { 0x5800 }, + { 0x6800 }, + { 0x7800 }, + { 0x8800 }, + { 0x9800 }, + { 0xa800 }, + { 0xb800 }, + { 0xc800 }, + { 0xd800 }, + { 0xe800 }, + { 0xf800 } +}; + +#define MEM_MASK 64 +struct { + unsigned char mem_index; + unsigned long mem_start; + unsigned char num_pages; +} mem_table[] = { + { 16, 0x0c0000, 40 }, + { 18, 0x0c4000, 40 }, + { 20, 0x0c8000, 40 }, + { 22, 0x0cc000, 40 }, + { 24, 0x0d0000, 40 }, + { 26, 0x0d4000, 40 }, + { 28, 0x0d8000, 40 }, + { 30, 0x0dc000, 40 }, + {144, 0xfc0000, 40 }, + {148, 0xfc8000, 40 }, + {154, 0xfd0000, 40 }, + {156, 0xfd8000, 40 }, + { 0, 0x0c0000, 20 }, + { 1, 0x0c2000, 20 }, + { 2, 0x0c4000, 20 }, + { 3, 0x0c6000, 20 } +}; + +#define IRQ_MASK 243 +struct { + unsigned char irq; + unsigned char ibm_irq; +} irq_table[] = { + { 3, 3 }, + { 4, 4 }, + { 10, 10 }, + { 14, 15 } +}; diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/Config.in linux-2.0.35-mca/drivers/scsi/Config.in --- linux/drivers/scsi/Config.in Sat Aug 8 21:30:23 1998 +++ linux-2.0.35-mca/drivers/scsi/Config.in Sat Aug 8 22:20:48 1998 @@ -76,6 +76,10 @@ fi fi fi +if [ "$CONFIG_MCA" = "y" ]; then + dep_tristate 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA $CONFIG_MCA + dep_tristate 'Future Domain MCS-600/700 (and IBM OEM) SCSI support' CONFIG_SCSI_FD_MCS $CONFIG_MCA +fi dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI if [ "$CONFIG_SCSI_PPA" != "n" ]; then bool ' Buggy EPP chipset support' CONFIG_SCSI_PPA_HAVE_PEDANTIC diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/Makefile linux-2.0.35-mca/drivers/scsi/Makefile --- linux/drivers/scsi/Makefile Sat Aug 8 21:30:24 1998 +++ linux-2.0.35-mca/drivers/scsi/Makefile Sat Aug 8 22:20:48 1998 @@ -323,6 +323,22 @@ endif endif +ifeq ($(CONFIG_SCSI_IBMMCA),y) +L_OBJS += ibmmca.o +else + ifeq ($(CONFIG_SCSI_IBMMCA),m) + M_OBJS += ibmmca.o + endif +endif + +ifeq ($(CONFIG_SCSI_FD_MCS),y) +L_OBJS += fd_mcs.o +else + ifeq ($(CONFIG_SCSI_FD_MCS),m) + M_OBJS += fd_mcs.o + endif +endif + ifeq ($(CONFIG_SCSI_T128),y) L_OBJS += t128.o else diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/fd_mcs.c linux-2.0.35-mca/drivers/scsi/fd_mcs.c --- linux/drivers/scsi/fd_mcs.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/scsi/fd_mcs.c Sat Aug 8 22:20:48 1998 @@ -0,0 +1,1475 @@ +/* fd_mcs.c -- Future Domain MCS 600/700 (or IBM OEM) driver + * + * FutureDomain MCS-600/700 v0.2 03/11/1998 by ZP Gu (zpg@castle.net) + * + * This driver is cloned from fdomain.* to specifically support + * the Future Domain MCS 600/700 MCA SCSI adapters. Some PS/2s + * also equipped with IBM Fast SCSI Adapter/A which is an OEM + * of MCS 700. + * + * This driver also supports Reply SB16/SCSI card (the SCSI part). + * + * What makes this driver different is that this driver is MCA only + * and it supports multiple adapters in the same system, IRQ + * sharing, some driver statistics, and maps highest SCSI id to sda. + * All cards are auto-detected. + * + * Assumptions: TMC-1800/18C50/18C30, BIOS >= 3.4 + * + * LILO command-line options: + * fd_mcs=[,] + * + * ******************************************************** + * Please see Copyrights/Comments in fdomain.* for credits. + * Following is from fdomain.c for acknowledgement: + * + * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu + * Revised: Wed Oct 2 11:10:55 1996 by r.faith@ieee.org + * Author: Rickard E. Faith, faith@cs.unc.edu + * Copyright 1992, 1993, 1994, 1995, 1996 Rickard E. Faith + * + * $Id: fdomain.c,v 5.45 1996/10/02 15:13:06 root Exp $ + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + + ************************************************************************** + + NOTES ON USER DEFINABLE OPTIONS: + + DEBUG: This turns on the printing of various debug information. + + ENABLE_PARITY: This turns on SCSI parity checking. With the current + driver, all attached devices must support SCSI parity. If none of your + devices support parity, then you can probably get the driver to work by + turning this option off. I have no way of testing this, however, and it + would appear that no one ever uses this option. + + FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the + 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by + the SCSI device, an interrupt will be raised. Therefore, this could be as + low as 0, or as high as 16. Note, however, that values which are too high + or too low seem to prevent any interrupts from occurring, and thereby lock + up the machine. I have found that 2 is a good number, but throughput may + be increased by changing this value to values which are close to 2. + Please let me know if you try any different values. + [*****Now a runtime option*****] + + RESELECTION: This is no longer an option, since I gave up trying to + implement it in version 4.x of this driver. It did not improve + performance at all and made the driver unstable (because I never found one + of the two race conditions which were introduced by the multiple + outstanding command code). The instability seems a very high price to pay + just so that you don't have to wait for the tape to rewind. If you want + this feature implemented, send me patches. I'll be happy to send a copy + of my (broken) driver to anyone who would like to see a copy. + + **************************************************************************/ + +#ifdef PCMCIA +#define MODULE +#endif + +#ifdef MODULE +#include +#endif + +#ifdef PCMCIA +#undef MODULE +#endif + +#include +#include +#include +#include "scsi.h" +#include "hosts.h" +#include "fd_mcs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_VERSION "v0.2 by ZP Gu" + +struct proc_dir_entry proc_scsi_fd_mcs = { + PROC_SCSI_FD_MCS, 6, "fd_mcs", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +/* START OF USER DEFINABLE OPTIONS */ + +#define DEBUG 0 /* Enable debugging output */ +#define ENABLE_PARITY 1 /* Enable SCSI Parity */ +#define DO_DETECT 0 /* Do device detection here (see scsi.c) */ + +/* END OF USER DEFINABLE OPTIONS */ + +#if DEBUG +#define EVERY_ACCESS 1 /* Write a line on every scsi access */ +#define ERRORS_ONLY 1 /* Only write a line if there is an error */ +#define DEBUG_DETECT 0 /* Debug fd_mcs_detect() */ +#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */ +#define DEBUG_ABORT 1 /* Debug abort() routine */ +#define DEBUG_RESET 1 /* Debug reset() routine */ +#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */ +#else +#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */ +#define ERRORS_ONLY 0 +#define DEBUG_DETECT 0 +#define DEBUG_MESSAGES 0 +#define DEBUG_ABORT 0 +#define DEBUG_RESET 0 +#define DEBUG_RACE 0 +#endif + +/* Errors are reported on the line, so we don't need to report them again */ +#if EVERY_ACCESS +#undef ERRORS_ONLY +#define ERRORS_ONLY 0 +#endif + +#if ENABLE_PARITY +#define PARITY_MASK 0x08 +#else +#define PARITY_MASK 0x00 +#endif + +enum chip_type { + unknown = 0x00, + tmc1800 = 0x01, + tmc18c50 = 0x02, + tmc18c30 = 0x03, +}; + +enum { + in_arbitration = 0x02, + in_selection = 0x04, + in_other = 0x08, + disconnect = 0x10, + aborted = 0x20, + sent_ident = 0x40, +}; + +enum in_port_type { + Read_SCSI_Data = 0, + SCSI_Status = 1, + TMC_Status = 2, + FIFO_Status = 3, /* tmc18c50/tmc18c30 only */ + Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */ + LSB_ID_Code = 5, + MSB_ID_Code = 6, + Read_Loopback = 7, + SCSI_Data_NoACK = 8, + Interrupt_Status = 9, + Configuration1 = 10, + Configuration2 = 11, /* tmc18c50/tmc18c30 only */ + Read_FIFO = 12, + FIFO_Data_Count = 14 +}; + +enum out_port_type { + Write_SCSI_Data = 0, + SCSI_Cntl = 1, + Interrupt_Cntl = 2, + SCSI_Mode_Cntl = 3, + TMC_Cntl = 4, + Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */ + Write_Loopback = 7, + IO_Control = 11, /* tmc18c30 only */ + Write_FIFO = 12 +}; + +struct fd_hostdata { + unsigned long _bios_base; + int _bios_major; + int _bios_minor; + volatile int _in_command; + Scsi_Cmnd *_current_SC; + enum chip_type _chip; + int _adapter_mask; + int _fifo_count; /* Number of 512 byte blocks before INTR */ + + char _adapter_name[64]; +#if DEBUG_RACE + volatile int _in_interrupt_flag; +#endif + + int _SCSI_Mode_Cntl_port; + int _FIFO_Data_Count_port; + int _Interrupt_Cntl_port; + int _Interrupt_Status_port; + int _Interrupt_Cond_port; + int _Read_FIFO_port; + int _Read_SCSI_Data_port; + int _SCSI_Cntl_port; + int _SCSI_Data_NoACK_port; + int _SCSI_Status_port; + int _TMC_Cntl_port; + int _TMC_Status_port; + int _Write_FIFO_port; + int _Write_SCSI_Data_port; + + int _FIFO_Size; /* = 0x2000; 8k FIFO for + pre-tmc18c30 chips */ + /* simple stats */ + int _Bytes_Read; + int _Bytes_Written; + int _INTR_Processed; +}; + +#define FD_MAX_HOSTS 4 /* enough? */ + +#define HOSTDATA(shpnt) ((struct fd_hostdata *) shpnt->hostdata) +#define bios_base (HOSTDATA(shpnt)->_bios_base) +#define bios_major (HOSTDATA(shpnt)->_bios_major) +#define bios_minor (HOSTDATA(shpnt)->_bios_minor) +#define in_command (HOSTDATA(shpnt)->_in_command) +#define current_SC (HOSTDATA(shpnt)->_current_SC) +#define chip (HOSTDATA(shpnt)->_chip) +#define adapter_mask (HOSTDATA(shpnt)->_adapter_mask) +#define FIFO_COUNT (HOSTDATA(shpnt)->_fifo_count) +#define adapter_name (HOSTDATA(shpnt)->_adapter_name) +#if DEBUG_RACE +#define in_interrupt_flag (HOSTDATA(shpnt)->_in_interrupt_flag) +#endif +#define SCSI_Mode_Cntl_port (HOSTDATA(shpnt)->_SCSI_Mode_Cntl_port) +#define FIFO_Data_Count_port (HOSTDATA(shpnt)->_FIFO_Data_Count_port) +#define Interrupt_Cntl_port (HOSTDATA(shpnt)->_Interrupt_Cntl_port) +#define Interrupt_Status_port (HOSTDATA(shpnt)->_Interrupt_Status_port) +#define Interrupt_Cond_port (HOSTDATA(shpnt)->_Interrupt_Cond_port) +#define Read_FIFO_port (HOSTDATA(shpnt)->_Read_FIFO_port) +#define Read_SCSI_Data_port (HOSTDATA(shpnt)->_Read_SCSI_Data_port) +#define SCSI_Cntl_port (HOSTDATA(shpnt)->_SCSI_Cntl_port) +#define SCSI_Data_NoACK_port (HOSTDATA(shpnt)->_SCSI_Data_NoACK_port) +#define SCSI_Status_port (HOSTDATA(shpnt)->_SCSI_Status_port) +#define TMC_Cntl_port (HOSTDATA(shpnt)->_TMC_Cntl_port) +#define TMC_Status_port (HOSTDATA(shpnt)->_TMC_Status_port) +#define Write_FIFO_port (HOSTDATA(shpnt)->_Write_FIFO_port) +#define Write_SCSI_Data_port (HOSTDATA(shpnt)->_Write_SCSI_Data_port) +#define FIFO_Size (HOSTDATA(shpnt)->_FIFO_Size) +#define Bytes_Read (HOSTDATA(shpnt)->_Bytes_Read) +#define Bytes_Written (HOSTDATA(shpnt)->_Bytes_Written) +#define INTR_Processed (HOSTDATA(shpnt)->_INTR_Processed) + +extern void fd_mcs_intr( int irq, void *dev_id, + struct pt_regs * regs ); + +struct fd_mcs_adapters_struct { + char* name; + int id; + enum chip_type fd_chip; + int fifo_size; + int fifo_count; +}; + +#define REPLY_ID 0x5137 + +static struct fd_mcs_adapters_struct fd_mcs_adapters[] = { + { "Future Domain SCSI Adapter MCS-700(18C50)", + 0x60e9, + tmc18c50, + 0x2000, + 4 }, + { "Future Domain SCSI Adapter MCS-600/700(TMC-1800)", + 0x6127, + tmc1800, + 0x2000, + 4 }, + { "Reply Sound Blaster/SCSI Adapter", + REPLY_ID, + tmc18c30, + 0x800, + 2 }, + { NULL, 0, 0, 0, 0 }, +}; + +static unsigned long addresses[] = {0xc8000, 0xca000, 0xce000, 0xde000}; +static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 }; +static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 }; + +/* host information */ +static int found = 0; +static struct Scsi_Host *hosts[FD_MAX_HOSTS+1] = { NULL }; + +static int user_fifo_count = 0; +static int user_fifo_size = 0; + +void fd_mcs_setup( char *str, int *ints ) +{ + static int done_setup = 0; + + if (done_setup++ || ints[0] < 1 || ints[0] > 2 || + ints[1] < 1 || ints[1] > 16) { + printk( "fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n" ); + } + + user_fifo_count = ints[0] >= 1 ? ints[1] : 0; + user_fifo_size = ints[0] >= 2 ? ints[2] : 0; +} + +static void print_banner( struct Scsi_Host *shpnt ) +{ + printk( "scsi%d : ", shpnt->host_no); + + if (bios_base) { + printk( "BIOS at 0x%lx", bios_base); + } else { + printk( "No BIOS"); + } + + printk( ", HostID %d, %s Chip, IRQ %d, IO 0x%x\n", + shpnt->this_id, + chip == tmc18c50 ? "TMC-18C50" + : (chip == tmc18c30 ? "TMC-18C30" : + (chip == tmc1800 ? "TMC-1800" : "Unknown")), + shpnt->irq, + shpnt->io_port ); +} + + +static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */ +{ + do { + udelay(10*1000); + } while (--amount); +} + +inline static void fd_mcs_make_bus_idle( struct Scsi_Host *shpnt ) +{ + outb( 0, SCSI_Cntl_port ); + outb( 0, SCSI_Mode_Cntl_port ); + if (chip == tmc18c50 || chip == tmc18c30) + outb( 0x21 | PARITY_MASK, TMC_Cntl_port ); /* Clear forced intr. */ + else + outb( 0x01 | PARITY_MASK, TMC_Cntl_port ); +} + +int fd_mcs_detect( Scsi_Host_Template *tpnt ) +{ + int loop; + struct Scsi_Host *shpnt; + + /* get id, port, bios, irq */ + int slot; + u_char pos2, pos3, pos4; + int id, port, irq; + unsigned long bios; + + /* changeable? */ + id = 7; + + /* if not MCA machine, return */ + if (!MCA_bus) + return 0; + + for( loop = 0; fd_mcs_adapters[loop].name != NULL; loop++ ) { + slot = 0; + while ( MCA_NOTFOUND != + (slot = mca_find_adapter(fd_mcs_adapters[loop].id, + slot)) ) { + + /* if we get this far, an adapter has been detected and is + enabled */ + + printk("scsi : %s at slot %d\n", + fd_mcs_adapters[loop].name, slot + 1 ); + + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + pos4 = mca_read_stored_pos( slot, 4); + + /* ready for next probe */ + slot++; + + if (fd_mcs_adapters[loop].id == REPLY_ID) { /* reply card */ + static int reply_irq[] = {10, 11, 14, 15}; + + bios = 0; /* no bios */ + + if (pos2 & 0x2) + port = ports[pos4 & 0x3]; + else + continue; + + /* can't really disable it, same as irq=10 */ + irq = reply_irq[((pos4 >> 2) & 0x1) + 2*((pos4 >> 4) & 0x1)]; + } else { + bios = addresses[pos2 >> 6]; + port = ports[(pos2 >> 4) & 0x03]; + irq = ints[(pos2 >> 1) & 0x07]; + } + + if (irq) { + /* claim the slot */ + mca_set_adapter_name( slot-1, fd_mcs_adapters[loop].name ); + + /* check irq/region */ + if (check_region(port, 0x10) || + request_irq(irq, fd_mcs_intr, + SA_INTERRUPT | SA_SHIRQ, "fd_mcs", hosts)) { + printk( "fd_mcs: check_region() || request_irq() failed, Skip it\n"); + + continue; + } + + /* register */ + if (!(shpnt = scsi_register(tpnt, sizeof(struct fd_hostdata)))) { + printk( "fd_mcs: scsi_register() failed\n"); + continue; + } + + /* request I/O region */ + request_region( port, 0x10, "fd_mcs" ); + + /* save name */ + strcpy(adapter_name, fd_mcs_adapters[loop].name); + + /* chip/fifo */ + chip = fd_mcs_adapters[loop].fd_chip; + /* use boot time value if available */ + FIFO_COUNT = + user_fifo_count?user_fifo_count:fd_mcs_adapters[loop].fifo_count; + FIFO_Size = + user_fifo_size?user_fifo_size:fd_mcs_adapters[loop].fifo_size; + +#ifdef NOT_USED + /* *************************************************** */ + /* Try to toggle 32-bit mode. This only + works on an 18c30 chip. (User reports + say this works, so we should switch to + it in the near future.) */ + outb( 0x80, port + IO_Control ); + if ((inb( port + Configuration2 ) & 0x80) == 0x80) { + outb( 0x00, port + IO_Control ); + if ((inb( port + Configuration2 ) & 0x80) == 0x00) { + chip = tmc18c30; + FIFO_Size = 0x800; /* 2k FIFO */ + + printk("FIRST: chip=%s, fifo_size=0x%x\n", + (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size); + } + } + + /* That should have worked, but appears to + have problems. Let's assume it is an + 18c30 if the RAM is disabled. */ + + if (inb( port + Configuration2 ) & 0x02) { + chip = tmc18c30; + FIFO_Size = 0x800; /* 2k FIFO */ + + printk("SECOND: chip=%s, fifo_size=0x%x\n", + (chip == tmc18c30)?"tmc18c30":"tmc18c50", FIFO_Size); + } + /* *************************************************** */ +#endif + + /* IBM/ANSI scsi scan ordering */ + shpnt->reverse_scan = 1; + + /* saving info */ + hosts[found++] = shpnt; + + shpnt->this_id = id; + shpnt->irq = irq; + shpnt->io_port = port; + shpnt->n_io_port = 0x10; + + /* save */ + bios_base = bios; + adapter_mask = (1 << id); + + /* save more */ + SCSI_Mode_Cntl_port = port + SCSI_Mode_Cntl; + FIFO_Data_Count_port = port + FIFO_Data_Count; + Interrupt_Cntl_port = port + Interrupt_Cntl; + Interrupt_Status_port = port + Interrupt_Status; + Interrupt_Cond_port = port + Interrupt_Cond; + Read_FIFO_port = port + Read_FIFO; + Read_SCSI_Data_port = port + Read_SCSI_Data; + SCSI_Cntl_port = port + SCSI_Cntl; + SCSI_Data_NoACK_port = port + SCSI_Data_NoACK; + SCSI_Status_port = port + SCSI_Status; + TMC_Cntl_port = port + TMC_Cntl; + TMC_Status_port = port + TMC_Status; + Write_FIFO_port = port + Write_FIFO; + Write_SCSI_Data_port = port + Write_SCSI_Data; + + Bytes_Read = 0; + Bytes_Written = 0; + INTR_Processed = 0; + + /* say something */ + print_banner( shpnt ); + + /* reset */ + outb( 1, SCSI_Cntl_port ); + do_pause( 2 ); + outb( 0, SCSI_Cntl_port ); + do_pause( 115 ); + outb( 0, SCSI_Mode_Cntl_port ); + outb( PARITY_MASK, TMC_Cntl_port ); + /* done reset */ + +#if DO_DETECT + /* scan devices attached */ + { + const int buflen = 255; + int i, j, retcode; + Scsi_Cmnd SCinit; + unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 }; + unsigned char do_request_sense[] = { REQUEST_SENSE, + 0, 0, 0, buflen, 0 }; + unsigned char do_read_capacity[] = { READ_CAPACITY, + 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + unsigned char buf[buflen]; + + SCinit.request_buffer = SCinit.buffer = buf; + SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1; + SCinit.use_sg = 0; + SCinit.lun = 0; + SCinit.host = shpnt; + + printk( "fd_mcs: detection routine scanning for devices:\n" ); + for (i = 0; i < 8; i++) { + if (i == shpnt->this_id) /* Skip host adapter */ + continue; + SCinit.target = i; + memcpy(SCinit.cmnd, do_request_sense, + sizeof(do_request_sense)); + retcode = fd_mcs_command(&SCinit); + if (!retcode) { + memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry)); + retcode = fd_mcs_command(&SCinit); + if (!retcode) { + printk( " SCSI ID %d: ", i ); + for (j = 8; j < (buf[4] < 32 ? buf[4] : 32); j++) + printk( "%c", buf[j] >= 20 ? buf[j] : ' ' ); + memcpy(SCinit.cmnd, do_read_capacity, + sizeof(do_read_capacity)); + retcode = fd_mcs_command(&SCinit); + if (!retcode) { + unsigned long blocks, size, capacity; + + blocks = (buf[0] << 24) | (buf[1] << 16) + | (buf[2] << 8) | buf[3]; + size = (buf[4] << 24) | (buf[5] << 16) | + (buf[6] << 8) | buf[7]; + capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L; + + printk( "%lu MB (%lu byte blocks)\n", + ((capacity + 5L) / 10L), size ); + } + } + } + } + } +#endif + } + } + + if (found == FD_MAX_HOSTS) { + printk( "fd_mcs: detecting reached max=%d host adapters.\n", + FD_MAX_HOSTS); + break; + } + } + + return found; +} + +const char *fd_mcs_info(struct Scsi_Host *shpnt) +{ + return adapter_name; +} + +static int TOTAL_INTR = 0; + +/* + * inout : decides on the direction of the dataflow and the meaning of the + * variables + * buffer: If inout==FALSE data is being written to it else read from it + * *start: If inout==FALSE start of the valid data in the buffer + * offset: If inout==FALSE offset from the beginning of the imaginary file + * from which we start writing into the buffer + * length: If inout==FALSE max number of bytes to be written into the buffer + * else number of bytes in the buffer + */ +int fd_mcs_proc_info( char *buffer, char **start, off_t offset, + int length, int hostno, int inout ) +{ + struct Scsi_Host *shpnt; + int len = 0; + int i; + + if (inout) + return(-ENOSYS); + + *start = buffer + offset; + + for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++); + shpnt = hosts[i]; + + if (!shpnt) { + return(-ENOENT); + } else { + len += sprintf(buffer+len, "Future Domain MCS-600/700 Driver %s\n", + DRIVER_VERSION); + + len += sprintf(buffer+len, "HOST #%d: %s\n", + hostno, adapter_name); + + len += sprintf(buffer+len, "FIFO Size=0x%x, FIFO Count=%d\n", + FIFO_Size, FIFO_COUNT); + + len += sprintf(buffer+len, "DriverCalls=%d, Interrupts=%d, BytesRead=%d, BytesWrite=%d\n\n", + TOTAL_INTR, INTR_Processed, Bytes_Read, Bytes_Written); + } + + if ((len -= offset) <= 0) + return 0; + if (len > length) + len = length; + return len; +} + +static int fd_mcs_select(struct Scsi_Host *shpnt, int target ) +{ + int status; + unsigned long timeout; + + outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */ + outb( adapter_mask | (1 << target), SCSI_Data_NoACK_port ); + + /* Stop arbitration and enable parity */ + outb( PARITY_MASK, TMC_Cntl_port ); + + timeout = 350; /* 350mS -- because of timeouts + (was 250mS) */ + + do { + status = inb( SCSI_Status_port ); /* Read adapter status */ + if (status & 1) { /* Busy asserted */ + /* Enable SCSI Bus (on error, should make bus idle with 0) */ + outb( 0x80, SCSI_Cntl_port ); + return 0; + } + udelay(1000); /* wait one msec */ + } while (--timeout); + + /* Make bus idle */ + fd_mcs_make_bus_idle(shpnt); +#if EVERY_ACCESS + if (!target) printk( "Selection failed\n" ); +#endif +#if ERRORS_ONLY + if (!target) { + static int flag = 0; + + if (!flag) /* Skip first failure for all chips. */ + ++flag; + else + printk( "fd_mcs: Selection failed\n" ); + } +#endif + return 1; +} + +static void my_done( struct Scsi_Host *shpnt, int error ) +{ + if (in_command) { + in_command = 0; + outb( 0x00, Interrupt_Cntl_port ); + fd_mcs_make_bus_idle(shpnt); + current_SC->result = error; + if (current_SC->scsi_done) + current_SC->scsi_done( current_SC ); + else panic( "fd_mcs: current_SC->scsi_done() == NULL" ); + } else { + panic( "fd_mcs: my_done() called outside of command\n" ); + } +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif +} + +void fd_mcs_intr( int irq, void *dev_id, struct pt_regs * regs ) +{ + int status; + int done = 0; + unsigned data_count; + + int i = 0; + struct Scsi_Host *shpnt; + + TOTAL_INTR++; + + /* search for one adapter-response on shared interrupt */ + while ((shpnt = hosts[i++])) { + if ((inb(TMC_Status_port)) & 1) + break; + } + + /* return if some other device on this IRQ caused the interrupt */ + if (!shpnt) { + return; + } + + INTR_Processed++; + + outb( 0x00, Interrupt_Cntl_port ); + + /* Abort calls my_done, so we do nothing here. */ + if (current_SC->SCp.phase & aborted) { +#if DEBUG_ABORT + printk( "Interrupt after abort, ignoring\n" ); +#endif + return; + } + +#if DEBUG_RACE + ++in_interrupt_flag; +#endif + + if (current_SC->SCp.phase & in_arbitration) { + status = inb( TMC_Status_port ); /* Read adapter status */ + if (!(status & 0x02)) { +#if EVERY_ACCESS + printk( " AFAIL " ); +#endif + my_done( shpnt, DID_BUS_BUSY << 16 ); + return; + } + current_SC->SCp.phase = in_selection; + + outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port ); + + outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */ + outb( adapter_mask | (1 << current_SC->target), SCSI_Data_NoACK_port ); + + /* Stop arbitration and enable parity */ + outb( 0x10 | PARITY_MASK, TMC_Cntl_port ); +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif + return; + } else if (current_SC->SCp.phase & in_selection) { + status = inb( SCSI_Status_port ); + if (!(status & 0x01)) { + /* Try again, for slow devices */ + if (fd_mcs_select(shpnt, current_SC->target )) { +#if EVERY_ACCESS + printk( " SFAIL " ); +#endif + my_done( shpnt, DID_NO_CONNECT << 16 ); + return; + } else { +#if EVERY_ACCESS + printk( " AltSel " ); +#endif + /* Stop arbitration and enable parity */ + outb( 0x10 | PARITY_MASK, TMC_Cntl_port ); + } + } + current_SC->SCp.phase = in_other; + outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port ); + outb( 0x80, SCSI_Cntl_port ); +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif + return; + } + + /* current_SC->SCp.phase == in_other: this is the body of the routine */ + + status = inb( SCSI_Status_port ); + + if (status & 0x10) { /* REQ */ + + switch (status & 0x0e) { + + case 0x08: /* COMMAND OUT */ + outb( current_SC->cmnd[current_SC->SCp.sent_command++], + Write_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "CMD = %x,", + current_SC->cmnd[ current_SC->SCp.sent_command - 1] ); +#endif + break; + case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */ + if (chip != tmc1800 && !current_SC->SCp.have_data_in) { + current_SC->SCp.have_data_in = -1; + outb( 0xd0 | PARITY_MASK, TMC_Cntl_port ); + } + break; + case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */ + if (chip != tmc1800 && !current_SC->SCp.have_data_in) { + current_SC->SCp.have_data_in = 1; + outb( 0x90 | PARITY_MASK, TMC_Cntl_port ); + } + break; + case 0x0c: /* STATUS IN */ + current_SC->SCp.Status = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Status = %x, ", current_SC->SCp.Status ); +#endif +#if ERRORS_ONLY + if (current_SC->SCp.Status + && current_SC->SCp.Status != 2 + && current_SC->SCp.Status != 8) { + printk( "ERROR fd_mcs: target = %d, command = %x, status = %x\n", + current_SC->target, + current_SC->cmnd[0], + current_SC->SCp.Status ); + } +#endif + break; + case 0x0a: /* MESSAGE OUT */ + outb( MESSAGE_REJECT, Write_SCSI_Data_port ); /* Reject */ + break; + case 0x0e: /* MESSAGE IN */ + current_SC->SCp.Message = inb( Read_SCSI_Data_port ); +#if EVERY_ACCESS + printk( "Message = %x, ", current_SC->SCp.Message ); +#endif + if (!current_SC->SCp.Message) ++done; +#if DEBUG_MESSAGES || EVERY_ACCESS + if (current_SC->SCp.Message) { + printk( "fd_mcs: message = %x\n", current_SC->SCp.Message ); + } +#endif + break; + } + } + + if (chip == tmc1800 + && !current_SC->SCp.have_data_in + && (current_SC->SCp.sent_command + >= current_SC->cmd_len)) { + /* We have to get the FIFO direction + correct, so I've made a table based + on the SCSI Standard of which commands + appear to require a DATA OUT phase. + */ + /* + p. 94: Command for all device types + CHANGE DEFINITION 40 DATA OUT + COMPARE 39 DATA OUT + COPY 18 DATA OUT + COPY AND VERIFY 3a DATA OUT + INQUIRY 12 + LOG SELECT 4c DATA OUT + LOG SENSE 4d + MODE SELECT (6) 15 DATA OUT + MODE SELECT (10) 55 DATA OUT + MODE SENSE (6) 1a + MODE SENSE (10) 5a + READ BUFFER 3c + RECEIVE DIAGNOSTIC RESULTS 1c + REQUEST SENSE 03 + SEND DIAGNOSTIC 1d DATA OUT + TEST UNIT READY 00 + WRITE BUFFER 3b DATA OUT + + p.178: Commands for direct-access devices (not listed on p. 94) + FORMAT UNIT 04 DATA OUT + LOCK-UNLOCK CACHE 36 + PRE-FETCH 34 + PREVENT-ALLOW MEDIUM REMOVAL 1e + READ (6)/RECEIVE 08 + READ (10) 3c + READ CAPACITY 25 + READ DEFECT DATA (10) 37 + READ LONG 3e + REASSIGN BLOCKS 07 DATA OUT + RELEASE 17 + RESERVE 16 DATA OUT + REZERO UNIT/REWIND 01 + SEARCH DATA EQUAL (10) 31 DATA OUT + SEARCH DATA HIGH (10) 30 DATA OUT + SEARCH DATA LOW (10) 32 DATA OUT + SEEK (6) 0b + SEEK (10) 2b + SET LIMITS (10) 33 + START STOP UNIT 1b + SYNCHRONIZE CACHE 35 + VERIFY (10) 2f + WRITE (6)/PRINT/SEND 0a DATA OUT + WRITE (10)/SEND 2a DATA OUT + WRITE AND VERIFY (10) 2e DATA OUT + WRITE LONG 3f DATA OUT + WRITE SAME 41 DATA OUT ? + + p. 261: Commands for sequential-access devices (not previously listed) + ERASE 19 + LOAD UNLOAD 1b + LOCATE 2b + READ BLOCK LIMITS 05 + READ POSITION 34 + READ REVERSE 0f + RECOVER BUFFERED DATA 14 + SPACE 11 + WRITE FILEMARKS 10 ? + + p. 298: Commands for printer devices (not previously listed) + ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) ***** + SLEW AND PRINT 0b DATA OUT -- same as seek + STOP PRINT 1b + SYNCHRONIZE BUFFER 10 + + p. 315: Commands for processor devices (not previously listed) + + p. 321: Commands for write-once devices (not previously listed) + MEDIUM SCAN 38 + READ (12) a8 + SEARCH DATA EQUAL (12) b1 DATA OUT + SEARCH DATA HIGH (12) b0 DATA OUT + SEARCH DATA LOW (12) b2 DATA OUT + SET LIMITS (12) b3 + VERIFY (12) af + WRITE (12) aa DATA OUT + WRITE AND VERIFY (12) ae DATA OUT + + p. 332: Commands for CD-ROM devices (not previously listed) + PAUSE/RESUME 4b + PLAY AUDIO (10) 45 + PLAY AUDIO (12) a5 + PLAY AUDIO MSF 47 + PLAY TRACK RELATIVE (10) 49 + PLAY TRACK RELATIVE (12) a9 + READ HEADER 44 + READ SUB-CHANNEL 42 + READ TOC 43 + + p. 370: Commands for scanner devices (not previously listed) + GET DATA BUFFER STATUS 34 + GET WINDOW 25 + OBJECT POSITION 31 + SCAN 1b + SET WINDOW 24 DATA OUT + + p. 391: Commands for optical memory devices (not listed) + ERASE (10) 2c + ERASE (12) ac + MEDIUM SCAN 38 DATA OUT + READ DEFECT DATA (12) b7 + READ GENERATION 29 + READ UPDATED BLOCK 2d + UPDATE BLOCK 3d DATA OUT + + p. 419: Commands for medium changer devices (not listed) + EXCHANGE MEDIUM 46 + INITIALIZE ELEMENT STATUS 07 + MOVE MEDIUM a5 + POSITION TO ELEMENT 2b + READ ELEMENT STATUS b8 + REQUEST VOL. ELEMENT ADDRESS b5 + SEND VOLUME TAG b6 DATA OUT + + p. 454: Commands for communications devices (not listed previously) + GET MESSAGE (6) 08 + GET MESSAGE (10) 28 + GET MESSAGE (12) a8 + */ + + switch (current_SC->cmnd[0]) { + case CHANGE_DEFINITION: case COMPARE: case COPY: + case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: + case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: + + case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case WRITE_6: case WRITE_10: case WRITE_VERIFY: + case 0x3f: case 0x41: + + case 0xb1: case 0xb0: case 0xb2: + case 0xaa: case 0xae: + + case 0x24: + + case 0x38: case 0x3d: + + case 0xb6: + + case 0xea: /* alternate number for WRITE LONG */ + + current_SC->SCp.have_data_in = -1; + outb( 0xd0 | PARITY_MASK, TMC_Cntl_port ); + break; + + case 0x00: + default: + + current_SC->SCp.have_data_in = 1; + outb( 0x90 | PARITY_MASK, TMC_Cntl_port ); + break; + } + } + + if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */ + while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) { +#if EVERY_ACCESS + printk( "DC=%d, ", data_count ) ; +#endif + if (data_count > current_SC->SCp.this_residual) + data_count = current_SC->SCp.this_residual; + if (data_count > 0) { +#if EVERY_ACCESS + printk( "%d OUT, ", data_count ); +#endif + if (data_count == 1) { + Bytes_Written++; + + outb( *current_SC->SCp.ptr++, Write_FIFO_port ); + --current_SC->SCp.this_residual; + } else { + data_count >>= 1; + outsw( Write_FIFO_port, current_SC->SCp.ptr, data_count ); + current_SC->SCp.ptr += 2 * data_count; + Bytes_Written += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_count; + } + } + if (!current_SC->SCp.this_residual) { + if (current_SC->SCp.buffers_residual) { + --current_SC->SCp.buffers_residual; + ++current_SC->SCp.buffer; + current_SC->SCp.ptr = current_SC->SCp.buffer->address; + current_SC->SCp.this_residual = current_SC->SCp.buffer->length; + } else + break; + } + } + } + + if (current_SC->SCp.have_data_in == 1) { /* DATA IN */ + while ((data_count = inw( FIFO_Data_Count_port )) > 0) { +#if EVERY_ACCESS + printk( "DC=%d, ", data_count ); +#endif + if (data_count > current_SC->SCp.this_residual) + data_count = current_SC->SCp.this_residual; + if (data_count) { +#if EVERY_ACCESS + printk( "%d IN, ", data_count ); +#endif + if (data_count == 1) { + Bytes_Read++; + *current_SC->SCp.ptr++ = inb( Read_FIFO_port ); + --current_SC->SCp.this_residual; + } else { + data_count >>= 1; /* Number of words */ + insw( Read_FIFO_port, current_SC->SCp.ptr, data_count ); + current_SC->SCp.ptr += 2 * data_count; + Bytes_Read += 2 * data_count; + current_SC->SCp.this_residual -= 2 * data_count; + } + } + if (!current_SC->SCp.this_residual + && current_SC->SCp.buffers_residual) { + --current_SC->SCp.buffers_residual; + ++current_SC->SCp.buffer; + current_SC->SCp.ptr = current_SC->SCp.buffer->address; + current_SC->SCp.this_residual = current_SC->SCp.buffer->length; + } + } + } + + if (done) { +#if EVERY_ACCESS + printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in ); +#endif + +#if ERRORS_ONLY + if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) { + if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) { + unsigned char key; + unsigned char code; + unsigned char qualifier; + + key = (unsigned char)(*((char *)current_SC->request_buffer + 2)) + & 0x0f; + code = (unsigned char)(*((char *)current_SC->request_buffer + 12)); + qualifier = (unsigned char)(*((char *)current_SC->request_buffer + + 13)); + + if (key != UNIT_ATTENTION + && !(key == NOT_READY + && code == 0x04 + && (!qualifier || qualifier == 0x02 || qualifier == 0x01)) + && !(key == ILLEGAL_REQUEST && (code == 0x25 + || code == 0x24 + || !code))) + + printk( "fd_mcs: REQUEST SENSE " + "Key = %x, Code = %x, Qualifier = %x\n", + key, code, qualifier ); + } + } +#endif +#if EVERY_ACCESS + printk( "BEFORE MY_DONE. . ." ); +#endif + my_done( shpnt, + (current_SC->SCp.Status & 0xff) + | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) ); +#if EVERY_ACCESS + printk( "RETURNING.\n" ); +#endif + + } else { + if (current_SC->SCp.phase & disconnect) { + outb( 0xd0 | FIFO_COUNT, Interrupt_Cntl_port ); + outb( 0x00, SCSI_Cntl_port ); + } else { + outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port ); + } + } +#if DEBUG_RACE + in_interrupt_flag = 0; +#endif + return; +} + +int fd_mcs_release(struct Scsi_Host *shpnt) +{ + int i, this_host, irq_usage; + + release_region(shpnt->io_port, shpnt->n_io_port); + + this_host = -1; + irq_usage = 0; + for (i = 0; i < found; i++) { + if (shpnt == hosts[i]) + this_host = i; + if (shpnt->irq == hosts[i]->irq) + irq_usage++; + } + + /* only for the last one */ + if (1 == irq_usage) + free_irq(shpnt->irq, hosts); + + found--; + + for (i = this_host; i < found; i++) + hosts[i] = hosts[i+1]; + + return 0; +} + +int fd_mcs_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) +{ + struct Scsi_Host *shpnt = SCpnt->host; + + if (in_command) { + panic( "fd_mcs: fd_mcs_queue() NOT REENTRANT!\n" ); + } +#if EVERY_ACCESS + printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n", + SCpnt->target, + *(unsigned char *)SCpnt->cmnd, + SCpnt->use_sg, + SCpnt->request_bufflen ); +#endif + + fd_mcs_make_bus_idle(shpnt); + + SCpnt->scsi_done = done; /* Save this for the done function */ + current_SC = SCpnt; + + /* Initialize static data */ + + if (current_SC->use_sg) { + current_SC->SCp.buffer = + (struct scatterlist *)current_SC->request_buffer; + current_SC->SCp.ptr = current_SC->SCp.buffer->address; + current_SC->SCp.this_residual = current_SC->SCp.buffer->length; + current_SC->SCp.buffers_residual = current_SC->use_sg - 1; + } else { + current_SC->SCp.ptr = (char *)current_SC->request_buffer; + current_SC->SCp.this_residual = current_SC->request_bufflen; + current_SC->SCp.buffer = NULL; + current_SC->SCp.buffers_residual = 0; + } + + + current_SC->SCp.Status = 0; + current_SC->SCp.Message = 0; + current_SC->SCp.have_data_in = 0; + current_SC->SCp.sent_command = 0; + current_SC->SCp.phase = in_arbitration; + + /* Start arbitration */ + outb( 0x00, Interrupt_Cntl_port ); + outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */ + outb( adapter_mask, SCSI_Data_NoACK_port ); /* Set our id bit */ + ++in_command; + outb( 0x20, Interrupt_Cntl_port ); + outb( 0x14 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */ + + return 0; +} + +static void internal_done( Scsi_Cmnd *SCpnt ) +{ + SCpnt->SCp.Status++; +} + +int fd_mcs_command( Scsi_Cmnd *SCpnt ) +{ + fd_mcs_queue( SCpnt, internal_done ); + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier(); + return SCpnt->result; +} + +#if DEBUG_ABORT || DEBUG_RESET +static void fd_mcs_print_info( Scsi_Cmnd *SCpnt ) +{ + unsigned int imr; + unsigned int irr; + unsigned int isr; + struct Scsi_Host *shpnt = SCpnt->host; + + if (!SCpnt || !SCpnt->host) { + printk( "fd_mcs: cannot provide detailed information\n" ); + } + + printk( "%s\n", fd_mcs_info( SCpnt->host ) ); + print_banner( SCpnt->host ); + switch (SCpnt->SCp.phase) { + case in_arbitration: printk( "arbitration " ); break; + case in_selection: printk( "selection " ); break; + case in_other: printk( "other " ); break; + default: printk( "unknown " ); break; + } + + printk( "(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n", + SCpnt->SCp.phase, + SCpnt->target, + *(unsigned char *)SCpnt->cmnd, + SCpnt->use_sg, + SCpnt->request_bufflen ); + printk( "sent_command = %d, have_data_in = %d, timeout = %d\n", + SCpnt->SCp.sent_command, + SCpnt->SCp.have_data_in, + SCpnt->timeout ); +#if DEBUG_RACE + printk( "in_interrupt_flag = %d\n", in_interrupt_flag ); +#endif + + imr = (inb( 0x0a1 ) << 8) + inb( 0x21 ); + outb( 0x0a, 0xa0 ); + irr = inb( 0xa0 ) << 8; + outb( 0x0a, 0x20 ); + irr += inb( 0x20 ); + outb( 0x0b, 0xa0 ); + isr = inb( 0xa0 ) << 8; + outb( 0x0b, 0x20 ); + isr += inb( 0x20 ); + + /* Print out interesting information */ + printk( "IMR = 0x%04x", imr ); + if (imr & (1 << shpnt->irq)) + printk( " (masked)" ); + printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr ); + + printk( "SCSI Status = 0x%02x\n", inb( SCSI_Status_port ) ); + printk( "TMC Status = 0x%02x", inb( TMC_Status_port ) ); + if (inb( TMC_Status_port ) & 1) + printk( " (interrupt)" ); + printk( "\n" ); + printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) ); + if (inb( Interrupt_Status_port ) & 0x08) + printk( " (enabled)" ); + printk( "\n" ); + if (chip == tmc18c50 || chip == tmc18c30) { + printk( "FIFO Status = 0x%02x\n", inb( shpnt->io_port + FIFO_Status ) ); + printk( "Int. Condition = 0x%02x\n", + inb( shpnt->io_port + Interrupt_Cond ) ); + } + printk( "Configuration 1 = 0x%02x\n", inb( shpnt->io_port + Configuration1 ) ); + if (chip == tmc18c50 || chip == tmc18c30) + printk( "Configuration 2 = 0x%02x\n", + inb( shpnt->io_port + Configuration2 ) ); +} +#endif + +int fd_mcs_abort( Scsi_Cmnd *SCpnt) +{ + struct Scsi_Host *shpnt = SCpnt->host; + + unsigned long flags; +#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT + printk( "fd_mcs: abort " ); +#endif + + save_flags( flags ); + cli(); + if (!in_command) { +#if EVERY_ACCESS || ERRORS_ONLY + printk( " (not in command)\n" ); +#endif + restore_flags( flags ); + return SCSI_ABORT_NOT_RUNNING; + } else printk( "\n" ); + +#if DEBUG_ABORT + fd_mcs_print_info( SCpnt ); +#endif + + fd_mcs_make_bus_idle(shpnt); + + current_SC->SCp.phase |= aborted; + + current_SC->result = DID_ABORT << 16; + + restore_flags( flags ); + + /* Aborts are not done well. . . */ + my_done( shpnt, DID_ABORT << 16 ); + + return SCSI_ABORT_SUCCESS; +} + +int fd_mcs_reset( Scsi_Cmnd *SCpnt, unsigned int reset_flags ) +{ + struct Scsi_Host *shpnt = SCpnt->host; + +#if DEBUG_RESET + static int called_once = 0; +#endif + +#if ERRORS_ONLY + if (SCpnt) printk( "fd_mcs: SCSI Bus Reset\n" ); +#endif + +#if DEBUG_RESET + if (called_once) fd_mcs_print_info( current_SC ); + called_once = 1; +#endif + + outb( 1, SCSI_Cntl_port ); + do_pause( 2 ); + outb( 0, SCSI_Cntl_port ); + do_pause( 115 ); + outb( 0, SCSI_Mode_Cntl_port ); + outb( PARITY_MASK, TMC_Cntl_port ); + + /* Unless this is the very first call (i.e., SCPnt == NULL), everything + is probably hosed at this point. We will, however, try to keep + things going by informing the high-level code that we need help. */ + + return SCSI_RESET_WAKEUP; +} + +#include "sd.h" +#include + +int fd_mcs_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array ) +{ + int drive; + unsigned char buf[512 + sizeof( int ) * 2]; + int size = disk->capacity; + int *sizes = (int *)buf; + unsigned char *data = (unsigned char *)(sizes + 2); + unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 }; + int retcode; + + /* BIOS >= 3.4 for MCA cards */ + drive = MINOR(dev) / 16; + + /* This algorithm was provided by Future Domain (much thanks!). */ + + sizes[0] = 0; /* zero bytes out */ + sizes[1] = 512; /* one sector in */ + memcpy( data, do_read, sizeof( do_read ) ); + retcode = kernel_scsi_ioctl( disk->device, + SCSI_IOCTL_SEND_COMMAND, + (void *)buf ); + if (!retcode /* SCSI command ok */ + && data[511] == 0xaa && data[510] == 0x55 /* Partition table valid */ + && data[0x1c2]) { /* Partition type */ + + /* The partition table layout is as follows: + + Start: 0x1b3h + Offset: 0 = partition status + 1 = starting head + 2 = starting sector and cylinder (word, encoded) + 4 = partition type + 5 = ending head + 6 = ending sector and cylinder (word, encoded) + 8 = starting absolute sector (double word) + c = number of sectors (double word) + Signature: 0x1fe = 0x55aa + + So, this algorithm assumes: + 1) the first partition table is in use, + 2) the data in the first entry is correct, and + 3) partitions never divide cylinders + + Note that (1) may be FALSE for NetBSD (and other BSD flavors), + as well as for Linux. Note also, that Linux doesn't pay any + attention to the fields that are used by this algorithm -- it + only uses the absolute sector data. Recent versions of Linux's + fdisk(1) will fill this data in correctly, and forthcoming + versions will check for consistency. + + Checking for a non-zero partition type is not part of the + Future Domain algorithm, but it seemed to be a reasonable thing + to do, especially in the Linux and BSD worlds. */ + + info_array[0] = data[0x1c3] + 1; /* heads */ + info_array[1] = data[0x1c4] & 0x3f; /* sectors */ + } else { + + /* Note that this new method guarantees that there will always be + less than 1024 cylinders on a platter. This is good for drives + up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */ + + if ((unsigned int)size >= 0x7e0000U) { + info_array[0] = 0xff; /* heads = 255 */ + info_array[1] = 0x3f; /* sectors = 63 */ + } else if ((unsigned int)size >= 0x200000U) { + info_array[0] = 0x80; /* heads = 128 */ + info_array[1] = 0x3f; /* sectors = 63 */ + } else { + info_array[0] = 0x40; /* heads = 64 */ + info_array[1] = 0x20; /* sectors = 32 */ + } + } + /* For both methods, compute the cylinders */ + info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] ); + + + return 0; +} + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = FD_MCS; + +#include "scsi_module.c" +#endif diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/fd_mcs.h linux-2.0.35-mca/drivers/scsi/fd_mcs.h --- linux/drivers/scsi/fd_mcs.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/scsi/fd_mcs.h Sat Aug 8 22:20:48 1998 @@ -0,0 +1,61 @@ +/* fd_mcs.h -- Header for Future Domain MCS 600/700 (or IBM OEM) driver + * + * fd_mcs.h v0.2 03/11/1998 ZP Gu (zpg@castle.net) + * + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef _FD_MCS_H +#define _FD_MCS_H + +extern int fd_mcs_detect( Scsi_Host_Template * ); +extern int fd_mcs_release( struct Scsi_Host * ); +extern int fd_mcs_command( Scsi_Cmnd * ); +extern int fd_mcs_abort( Scsi_Cmnd * ); +extern int fd_mcs_reset( Scsi_Cmnd *, unsigned int ); +extern int fd_mcs_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) ); +extern int fd_mcs_biosparam( Disk *, kdev_t, int * ); +extern int fd_mcs_proc_info( char *, char **, off_t, int, int, int ); +extern const char *fd_mcs_info(struct Scsi_Host *); + + +extern struct proc_dir_entry proc_scsi_fd_mcs; + +#define FD_MCS {\ + NULL, \ + NULL, \ + &proc_scsi_fd_mcs,\ + fd_mcs_proc_info, \ + "FD_MCS", \ + fd_mcs_detect, \ + fd_mcs_release, \ + fd_mcs_info, \ + fd_mcs_command, \ + fd_mcs_queue, \ + fd_mcs_abort, \ + fd_mcs_reset, \ + NULL, \ + fd_mcs_biosparam, \ + 1, \ + 6, \ + 64, \ + 1, \ + 0, \ + 0, \ + DISABLE_CLUSTERING } + +#endif /* _FD_MCS_H */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/hosts.c linux-2.0.35-mca/drivers/scsi/hosts.c --- linux/drivers/scsi/hosts.c Sat Aug 8 21:36:11 1998 +++ linux-2.0.35-mca/drivers/scsi/hosts.c Sat Aug 8 22:20:48 1998 @@ -145,6 +145,14 @@ #include "wd7000.h" #endif +#ifdef CONFIG_SCSI_IBMMCA +#include "ibmmca.h" +#endif + +#ifdef CONFIG_SCSI_FD_MCS +#include "fd_mcs.h" +#endif + #ifdef CONFIG_SCSI_EATA #include "eata.h" #endif @@ -233,6 +241,19 @@ #endif #endif +/* Detect SCSI in the order of IBM/FD/BT/AHA on PS/2 MCA systems */ +/* Moved the following to the front for MCA detection. */ +/* FD_MCA is enough for FD cards, but FDOMAIN_16X0 here in case */ +#ifdef CONFIG_SCSI_IBMMCA + IBMMCA, +#endif +#ifdef CONFIG_SCSI_FD_MCS + FD_MCS, +#endif +#ifdef CONFIG_SCSI_FUTURE_DOMAIN + FDOMAIN_16X0, +#endif + #ifdef CONFIG_SCSI_ADVANSYS ADVANSYS, #endif @@ -258,9 +279,6 @@ #ifdef CONFIG_SCSI_AIC7XXX AIC7XXX, #endif -#ifdef CONFIG_SCSI_FUTURE_DOMAIN - FDOMAIN_16X0, -#endif #ifdef CONFIG_SCSI_IN2000 IN2000, #endif @@ -331,6 +349,87 @@ #define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template)) +/* Runtime scsi adapter reordering: ZP Gu (zpg@castle.net) */ +/* The following enhancement will enable runtime reordering of */ +/* scsi host adapters by using the scsi-probe option, example: */ +/* scsi-probe=buslogic,fd_mcs,adaptec1542,ibmmca */ +/* limitations: This depends on the driver name provided by */ +/* individual scsi drivers. It would be helpful if all drivers */ +/* provide a unique name. These 3 characters are ignored when */ +/* comparing names: ' ', '-', '_' Case is ignored too. */ +/* You don't have to put the whole name as long as uniqueness */ +/* can be established, so scsi-probe=bus,fdmc is the same as */ +/* scsi-probe=buslogic,fd_mcs */ + +/* holds user preference if any */ +static int host_scan_order[MAX_SCSI_HOSTS] = {-1}; + +/* fuzzy name matching */ +static int namematch(const char *str1, char *str2) +{ + int c1, c2; + + if (!str1 || !str2 || !(*str1) || !(*str2)) + return 0; + + /* step thru, ignore irrelevant chars */ + while (1) { + while (*str1 == ' ' || *str1 == '-' || *str1 == '_') + str1++; + while (*str2 == ' ' || *str2 == '-' || *str2 == '_') + str2++; + + if (!(*str1) || !(*str2)) + return 1; + + /* convert to uppercase */ + c1 = (*str1 >= 'a' && *str1 <= 'z') ? *str1 - ('a'-'A') : *str1; + c2 = (*str2 >= 'a' && *str2 <= 'z') ? *str2 - ('a'-'A') : *str2; + + if (c1 != c2) + return 0; + else { + str1++; str2++; + } + } +} + +void scsi_host_order_setup(char *str, int *ints) +{ + static int save_order[MAX_SCSI_HOSTS]; + static char called = 0; + + if (!called++ && str && *str) { + int i, order = -1; + char *next; + + for (i = 0; i < MAX_SCSI_HOSTS; i++) + save_order[i] = -1; + + /* fill user preference first */ + next = str; + while ((str = next) != NULL) { + if ((next = strchr(str,',')) != NULL) + *next++ = 0; + + for (i = 0; i < MAX_SCSI_HOSTS; i++) + if (namematch(builtin_scsi_hosts[i].name, str)) { + if (save_order[i] == -1) /* ignore dup input */ + save_order[i] = ++order; + break; + } + } + + /* fill the rest */ + for (i = 0; i < MAX_SCSI_HOSTS; i++) + if (-1 == save_order[i]) + save_order[i] = ++order; + + /* invert array to put into scan order used by scsi_init() */ + for (i = 0; i < MAX_SCSI_HOSTS; i++) + host_scan_order[save_order[i]] = i; + } +} /* * Our semaphores and timeout counters, where size depends on @@ -393,6 +492,10 @@ retval->max_id = 8; retval->max_lun = 8; +#ifdef CONFIG_MCA + retval->reverse_scan = 0; +#endif + retval->unique_id = 0; retval->io_port = 0; retval->hostt = tpnt; @@ -444,8 +547,13 @@ if(called) return 0; called = 1; - for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++) + for (i = 0; i < MAX_SCSI_HOSTS; ++i) { + if (host_scan_order[0] != -1) + tpnt = &builtin_scsi_hosts[host_scan_order[i]]; + else + tpnt = &builtin_scsi_hosts[i]; + /* * Initialize our semaphores. -1 is interpreted to mean * "inactive" - where as 0 will indicate a time out condition. diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/hosts.h linux-2.0.35-mca/drivers/scsi/hosts.h --- linux/drivers/scsi/hosts.h Wed Oct 15 18:24:02 1997 +++ linux-2.0.35-mca/drivers/scsi/hosts.h Sat Aug 8 22:55:40 1998 @@ -259,6 +259,11 @@ unsigned int max_lun; unsigned int max_channel; +#ifdef CONFIG_MCA + char reverse_scan; /* 0: normal order, 1: reverse (IBM PS2) */ + /* added by ZP Gu (zpg@castle.net) */ +#endif + /* * Pointer to a circularly linked list - this indicates the hosts * that should be locked out of performing I/O while we have an active diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/ibmmca.c linux-2.0.35-mca/drivers/scsi/ibmmca.c --- linux/drivers/scsi/ibmmca.c Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/scsi/ibmmca.c Sat Aug 8 22:20:49 1998 @@ -0,0 +1,2192 @@ +/* + * Low Level Driver for the IBM Microchannel SCSI Subsystem + * + * Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU + * General Public License. Written by Martin Kolinek, December 1995. + */ + +/* Update history: + Jan 15 1996: First public release. + - Martin Kolinek + + Jan 23 1996: Scrapped code which reassigned scsi devices to logical + device numbers. Instead, the existing assignment (created + when the machine is powered-up or rebooted) is used. + A side effect is that the upper layer of Linux SCSI + device driver gets bogus scsi ids (this is benign), + and also the hard disks are ordered under Linux the + same way as they are under dos (i.e., C: disk is sda, + D: disk is sdb, etc.). + - Martin Kolinek + + I think that the CD-ROM is now detected only if a CD is + inside CD_ROM while Linux boots. This can be fixed later, + once the driver works on all types of PS/2's. + - Martin Kolinek + + Feb 7 1996: Modified biosparam function. Fixed the CD-ROM detection. + For now, devices other than harddisk and CD_ROM are + ignored. Temporarily modified abort() function + to behave like reset(). + - Martin Kolinek + + Mar 31 1996: The integrated scsi subsystem is correctly found + in PS/2 models 56,57, but not in model 76. Therefore + the ibmmca_scsi_setup() function has been added today. + This function allows the user to force detection of + scsi subsystem. The kernel option has format + ibmmcascsi=n + where n is the scsi_id (pun) of the subsystem. Most likely, n is 7. + - Martin Kolinek + + Aug 21 1996: Modified the code which maps ldns to (pun,0). It was + insufficient for those of us with CD-ROM changers. + - Chris Beauregard + + Dec 14 1996: More improvements to the ldn mapping. See check_devices + for details. Did more fiddling with the integrated SCSI detection, + but I think it's ultimately hopeless without actually testing the + model of the machine. The 56, 57, 76 and 95 (ultimedia) all have + different integrated SCSI register configurations. However, the 56 + and 57 are the only ones that have problems with forced detection. + - Chris Beauregard + + Mar 8-16 1997: Modified driver to run as a module and to support + multiple adapters. A structure, called ibmmca_hostdata, is now + present, containing all the variables, that were once only + available for one single adapter. The find_subsystem-routine has vanished. + The hardware recognition is now done in ibmmca_detect directly. + This routine checks for presence of MCA-bus, checks the interrupt + level and continues with checking the installed hardware. + Certain PS/2-models do not recognize a SCSI-subsystem automatically. + Hence, the setup defined by command-line-parameters is checked first. + Thereafter, the routine probes for an integrated SCSI-subsystem. + Finally, adapters are checked. This method has the advantage to cover all + possible combinations of multiple SCSI-subsystems on one MCA-board. Up to + eight SCSI-subsystems can be recognized and announced to the upper-level + drivers with this improvement. A set of defines made changes to other + routines as small as possible. + - Klaus Kudielka + + May 30 1997: (v1.5b) + 1) SCSI-command capability enlarged by the recognition of MODE_SELECT. + This needs the RD-Bit to be disabled on IM_OTHER_SCSI_CMD_CMD which + allows data to be written from the system to the device. It is a + necessary step to be allowed to set blocksize of SCSI-tape-drives and + the tape-speed, whithout confusing the SCSI-Subsystem. + 2) The recognition of a tape is included in the check_devices routine. + This is done by checking for TYPE_TAPE, that is already defined in + the kernel-scsi-environment. The markup of a tape is done in the + global ldn_is_tape[] array. If the entry on index ldn + is 1, there is a tapedrive connected. + 3) The ldn_is_tape[] array is necessary to distinguish between tape- and + other devices. Fixed blocklength devices should not cause a problem + with the SCB-command for read and write in the ibmmca_queuecommand + subroutine. Therefore, I only derivate the READ_XX, WRITE_XX for + the tape-devices, as recommended by IBM in this Technical Reference, + mentioned below. (IBM recommends to avoid using the read/write of the + subsystem, but the fact was, that read/write causes a command error from + the subsystem and this causes kernel-panic.) + 4) In addition, I propose to use the ldn instead of a fix char for the + display of PS2_DISK_LED_ON(). On 95, one can distinguish between the + devices that are accessed. It shows activity and easyfies debugging. + The tape-support has been tested with a SONY SDT-5200 and a HP DDS-2 + (I do not know yet the type). Optimization and CD-ROM audio-support, + I am working on ... + - Michael Lang + + June 19 1997: (v1.6b) + 1) Submitting the extra-array ldn_is_tape[] -> to the local ld[] + device-array. + 2) CD-ROM Audio-Play seems to work now. + 3) When using DDS-2 (120M) DAT-Tapes, mtst shows still density-code + 0x13 for ordinary DDS (61000 BPM) instead 0x24 for DDS-2. This appears + also on Adaptec 2940 adaptor in a PCI-System. Therefore, I assume that + the problem is independent of the low-level-driver/bus-architecture. + 4) Hexadecimal ldn on PS/2-95 LED-display. + 5) Fixing of the PS/2-LED on/off that it works right with tapedrives and + does not confuse the disk_rw_in_progress counter. + - Michael Lang + + June 21 1997: (v1.7b) + 1) Adding of a proc_info routine to inform in /proc/scsi/ibmmca/ the + outer-world about operational load statistics on the different ldns, + seen by the driver. Everybody that has more than one IBM-SCSI should + test this, because I only have one and cannot see what happens with more + than one IBM-SCSI hosts. + 2) Definition of a driver version-number to have a better recognition of + the source when there are existing too much releases that may confuse + the user, when reading about release-specific problems. Up to know, + I calculated the version-number to be 1.7. Because we are in BETA-test + yet, it is today 1.7b. + 3) Sorry for the heavy bug I programmed on June 19 1997! After that, the + CD-ROM did not work any more! The C7-command was a fake impression + I got while programming. Now, the READ and WRITE commands for CD-ROM are + no longer running over the subsystem, but just over + IM_OTHER_SCSI_CMD_CMD. On my observations (PS/2-95), now CD-ROM mounts + much faster(!) and hopefully all fancy multimedia-functions, like direct + digital recording from audio-CDs also work. (I tried it with cdda2wav + from the cdwtools-package and it filled up the harddisk immediately :-).) + To easify boolean logics, a further local device-type in ld[], called + is_cdrom has been included. + 4) If one uses a SCSI-device of unsupported type/commands, one + immediately runs into a kernel-panic caused by Command Error. To better + understand which SCSI-command caused the problem, I extended this + specific panic-message slightly. + - Michael Lang + + June 25 1997: (v1.8b) + 1) Some cosmetical changes for the handling of SCSI-device-types. + Now, also CD-Burners / WORMs and SCSI-scanners should work. For + MO-drives I have no experience, therefore not yet supported. + In logical_devices I changed from different type-variables to one + called 'device_type' where the values, corresponding to scsi.h, + of a SCSI-device are stored. + 2) There existed a small bug, that maps a device, coming after a SCSI-tape + wrong. Therefore, e.g. a CD-ROM changer would have been mapped wrong + -> problem removed. + 3) Extension of the logical_device structure. Now it contains also device, + vendor and revision-level of a SCSI-device for internal usage. + - Michael Lang + + June 26-29 1997: (v2.0b) + 1) The release number 2.0b is necessary because of the completely new done + recognition and handling of SCSI-devices with the adapter. As I got + from Chris the hint, that the subsystem can reassign ldns dynamically, + I remembered this immediate_assign-command, I found once in the handbook. + Now, the driver first kills all ldn assignments that are set by default + on the SCSI-subsystem. After that, it probes on all puns and luns for + devices by going through all combinations with immediate_assign and + probing for devices, using device_inquiry. The found physical(!) pun,lun + structure is stored in get_scsi[][] as device types. This is followed + by the assignment of all ldns to existing SCSI-devices. If more ldns + than devices are available, they are assigned to non existing pun,lun + combinations to satisfy the adapter. With this, the dynamical mapping + was possible to implement. (For further info see the text in the + source-code and in the description below. Read the description + below BEFORE installing this driver on your system!) + 2) Changed the name IBMMCA_DRIVER_VERSION to IBMMCA_SCSI_DRIVER_VERSION. + 3) The LED-display shows on PS/2-95 no longer the ldn, but the SCSI-ID + (pun) of the accessed SCSI-device. This is now senseful, because the + pun known within the driver is exactly the pun of the physical device + and no longer a fake one. + 4) The /proc/scsi/ibmmca/ consists now of the first part, where + hit-statistics of ldns is shown and a second part, where the maps of + physical and logical SCSI-devices are displayed. This could be very + interesting, when one is using more than 15 SCSI-devices in order to + follow the dynamical remapping of ldns. + - Michael Lang + + June 26-29 1997: (v2.0b-1) + 1) I forgot to switch the local_checking_phase_flag to 1 and back to 0 + in the dynamical remapping part in ibmmca_queuecommand for the + device_exist routine. Sorry. + - Michael Lang + + July 1-13 1997: (v3.0b,c) + 1) Merging of the driver-developments of Klaus Kudielka and Michael Lang + in order to get a optimum and unified driver-release for the + IBM-SCSI-Subsystem-Adapter(s). + For people, using the Kernel-release >=2.1.0, module-support should + be no problem. For users, running under <2.1.0, module-support may not + work, because the methods have changed between 2.0.x and 2.1.x. + 2) Added some more effective statistics for /proc-output. + 3) Change typecasting at necessary points from (unsigned long) to + virt_to_bus(). + 4) Included #if... at special points to have specific adaption of the + driver to kernel 2.0.x and 2.1.x. It should therefore also run with + later releases. + 5) Magneto-Optical drives and medium-changers are also recognized, now. + Therefore, we have a completely gapfree recognition of all SCSI- + device-types, that are known by Linux up to kernel 2.1.31. + 6) The flag SCSI_IBMMCA_DEV_RESET has been inserted. If it is set within + the configuration, each connected SCSI-device will get a reset command + during boottime. This can be necessary for some special SCSI-devices. + This flag should be included in Config.in. + (See also the new Config.in file.) + Probable next improvement: bad disk handler. + - Michael Lang + + Sept 14 1997: (v3.0c) + 1) Some debugging and speed optimization applied. + - Michael Lang + + + + TODO: + + - It seems that the handling of bad disks is really bad - + non-existent, in fact. + - More testing of the full driver-controlled dynamical ldn + (re)mapping for up to 56 SCSI-devices. + - Support more SCSI-device-types, if Linux defines more. + - Support more of the SCSI-command set. + - Support some of the caching abilities, particularly Read Prefetch. + This fetches data into the cache, which later gets hit by the + regular Read Data. + - Abort and Reset functions still slightly buggy. Especially when + floppydisk(!) operations report errors. + + Dec 15, 1997 + - chrisb@truespectra.com + - made the front panel display thingy optional, specified from the + command-line via ibmmcascsi=display. Along the lines of the /LED + option for the OS/2 driver. + - fixed small bug in the LED display that would hang some machines. + - reversed ordering of the drives (using the + IBMMCA_SCSI_ORDER_STANDARD define). This is necessary for two main + reasons: + - users who've already installed Linux won't be screwed. Keep + in mind that not everyone is a kernel hacker. + - be consistent with the BIOS ordering of the drives. In the + BIOS, id 6 is C:, id 0 might be D:. With this scheme, they'd be + backwards. This confuses the crap out of those heathens who've + got a impure Linux installation (which, , I'm one of). + This whole problem arises because IBM is actually non-standard with + the id to BIOS mappings. You'll find, in fdomain.c, a similar + comment about a few FD BIOS revisions. The Linux (and apparently + industry) standard is that C: maps to scsi id (0,0). Let's stick + with that standard. + - Since this is technically a branch of my own, I changed the + version number to 3.0e-cpb. + + March 11, 1998 [ZP Gu zpg@castle.net] + Modified to auto-detect on 56/57. Rearranged code and it started to + work in 56/57. Probably a timing and/or an interrupt handling issue. + Added option for disk mapping. Default is to use IBM/ANSI ordering + to map highest id to sda. Example: + ibmmcascsi=normal,0x3540 + will force detection at 0x3540 and map lowest id to sda, while + ibmmcascsi=0x3540 or no option at all + will use IBM/ANSI ordering. + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sd.h" +#include "scsi.h" +#include "hosts.h" +#include "ibmmca.h" + +#include /* for CONFIG_SCSI_IBMMCA etc. */ + +/*--------------------------------------------------------------------*/ + +/* current version of this driver-source: */ +#define IBMMCA_SCSI_DRIVER_VERSION "3.0f[zpg]" + +/* use standard Linux ordering, where C: maps to (0,0), unlike the IBM +standard which seems to like C: => (6,0) */ + +/* this is now a run time option, default is IBM/ANSI SCSI ordering. */ +/* if something like ibmmcascsi=normal[,0x3540] is used, the lowest */ +/* ID will be mapped to sda, just like on a clone (non IBM/ANSI). */ +static char ibm_ansi_order = 1; + +/* + Driver Description + + (A) Subsystem Detection + This is done in the ibmmca_detect() function and is easy, since + the information about MCA integrated subsystems and plug-in + adapters is readily available in structure *mca_info. + + (B) Physical Units, Logical Units, and Logical Devices + There can be up to 56 devices on SCSI bus (besides the adapter): + there are up to 7 "physical units" (each identified by physical unit + number or pun, also called the scsi id, this is the number you select + with hardware jumpers), and each physical unit can have up to 8 + "logical units" (each identified by logical unit number, or lun, + between 0 and 7). + + Typically the adapter has pun=7, so puns of other physical units + are between 0 and 6. Almost all physical units have only one + logical unit, with lun=0. A CD-ROM jukebox would be an example of + a physical unit with more than one logical unit. + + The embedded microprocessor of IBM SCSI subsystem hides the complex + two-dimensional (pun,lun) organization from the operating system. + When the machine is powered-up (or rebooted, I am not sure), the + embedded microprocessor checks, on it own, all 56 possible (pun,lun) + combinations, and first 15 devices found are assigned into a + one-dimensional array of so-called "logical devices", identified by + "logical device numbers" or ldn. The last ldn=15 is reserved for + the subsystem itself. + + One consequence of information hiding is that the real (pun,lun) + numbers are also hidden. Therefore this driver takes the following + approach: It checks the ldn's (0 to 6) to find out which ldn's + have devices assigned. This is done by function check_devices() and + device_exists(). The interrupt handler has a special paragraph of code + (see local_checking_phase_flag) to assist in the checking. Assume, for + example, that three logical devices were found assigned at ldn 0, 1, 2. + These are presented to the upper layer of Linux SCSI driver + as devices with bogus (pun, lun) equal to (0,0), (1,0), (2,0). + On the other hand, if the upper layer issues a command to device + say (4,0), this driver returns DID_NO_CONNECT error. + + That last paragraph is no longer correct, but is left for + historical purposes. It limited the number of devices to 7, far + fewer than the 15 that it could use. Now it just maps + ldn -> (ldn/8,ldn%8). We end up with a real mishmash of puns + and luns, but it all seems to work. - Chris Beaurgard + + And that last paragraph is also no longer correct. It uses a + slightly more complex mapping that will always map hard disks to + (x,0), for some x, and consecutive none disk devices will usually + share puns. + + Again, the last paragraphs are no longer correct. Now, the physical + SCSI-devices on the SCSI-bus are probed via immediate_assign- and + device_inquiry-commands. This delivers a exact map of the physical + SCSI-world that is now stored in the get_scsi[][]-array. This means, + that the once hidden pun,lun assignment is now known to this driver. + It no longer believes in default-settings of the subsystem and maps all + ldns to existing pun,lun by foot. This assures full control of the ldn + mapping and allows dynamical remapping of ldns to different pun,lun, if + there are more SCSI-devices installed than ldns available (n>15). The + ldns from 0 to 6 get 'hardwired' by this driver to puns 0 to 7 at lun=0, + excluding the pun of the subsystem. This assures, that at least simple + SCSI-installations have optimum access-speed and are not touched by + dynamical remapping. The ldns 7 to 14 are put to existing devices with + lun>0 or to non-existing devices, in order to satisfy the subsystem, if + there are less than 15 SCSI-devices connected. In the case of more than 15 + devices, the dynamical mapping goes active. If the get_scsi[][] reports a + device to be existant, but it has no ldn assigned, it gets a ldn out of 7 + to 14. The numbers are assigned in cyclic order. Therefore it takes 8 + dynamical assignments on SCSI-devices, until a certain device + looses its ldn again. This assures, that dynamical remapping is avoided + during intense I/O between up to eight SCSI-devices (means pun,lun + combinations). A further advantage of this method is, that people who + build their kernel without probing on all luns will get what they expect. + + IMPORTANT: Because of the now correct recognition of physical pun,lun, and + their report to mid-level- and higher-level-drivers, the new reported puns + can be different from the old, faked puns. Therefore, Linux will eventually + change /dev/sdXXX assignments and prompt you for corrupted superblock + repair on boottime. In this case DO NOT PANIC, YOUR DISKS ARE STILL OK!!! + You have to reboot (CTRL-D) with a old kernel and set the /etc/fstab-file + entries right. After that, the system should come up as errorfree as before. + If your boot-partition is not coming up, also edit the /etc/lilo.conf-file + in a Linux session booted on old kernel and run lilo before reboot. Check + lilo.conf anyway to get boot on other partitions with foreign OSes right + again. + + (C) Regular Processing + Only three functions get involved: ibmmca_queuecommand(), issue_cmd(), + and interrupt_handler(). + + The upper layer issues a scsi command by calling function + ibmmca_queuecommand(). This function fills a "subsystem control block" + (scb) and calls a local function issue_cmd(), which writes a scb + command into subsystem I/O ports. Once the scb command is carried out, + interrupt_handler() is invoked. If a device is determined to be existant + and it has not assigned any ldn, it gets one dynamically. + + (D) Abort, Reset. + These are implemented with busy waiting for interrupt to arrive. + The abort does not worked well for me, so I instead call the + ibmmca_reset() from the ibmmca_abort() function. + + (E) Disk Geometry + The ibmmca_biosparams() function should return same disk geometry + as bios. This is needed for fdisk, etc. The returned geometry is + certainly correct for disk smaller than 1 gigabyte, but I am not + 100% sure that it is correct for larger disks. + + (F) Kernel Boot Option + The function ibmmca_scsi_setup() is called if option ibmmcascsi=n + is passed to the kernel. See file linux/init/main.c for details. + + (G) Driver Module Support + Is implemented and tested by K. Kudielka. This could probably not work + on kernels <2.1.0. + + (H) Multiple Hostadapter Support + This driver supports up to eight interfaces of type IBM-SCSI-Subsystem. + Integrated-, and MCA-adapters are automatically recognized. Unrecognizable + IBM-SCSI-Subsystem interfaces can be specified as kernel-parameters. + + (I) /proc-Filesystem Information + Information about the driver condition is given in + /proc/scsi/ibmmca/. ibmmca_proc_info provides this information. + */ + +/*--------------------------------------------------------------------*/ +/* Here are the values and structures specific for the subsystem. + * The source of information is "Update for the PS/2 Hardware + * Interface Technical Reference, Common Interfaces", September 1991, + * part number 04G3281, available in the U.S. for $21.75 at + * 1-800-IBM-PCTB, elsewhere call your local friendly IBM + * representative. + * In addition to SCSI subsystem, this update contains fairly detailed + * (at hardware register level) sections on diskette controller, + * keyboard controller, serial port controller, VGA, and XGA. + * + * Additional information from "Personal System/2 Micro Channel SCSI + * Adapter with Cache Technical Reference", March 1990, PN 68X2365, + * probably available from the same source (or possibly found buried + * in officemates desk). + * + * Further literature/program-sources referred for this driver: + * + * Friedhelm Schmidt, "SCSI-Bus und IDE-Schnittstelle - Moderne Peripherie- + * Schnittstellen: Hardware, Protokollbeschreibung und Anwendung", 2. Aufl. + * Addison Wesley, 1996. + * + * Michael K. Johnson, "The Linux Kernel Hackers' Guide", Version 0.6, Chapel + * Hill - North Carolina, 1995 + * + * Andreas Kaiser, "SCSI TAPE BACKUP for OS/2 2.0", Version 2.12, Stuttgart + * 1993 + */ + +/*--------------------------------------------------------------------*/ + +/* driver configuration */ +#define IM_MAX_HOSTS 4 /* maximum number of host adapters */ +#define IM_RESET_DELAY 10 /* seconds allowed for a reset */ + +/* driver debugging - #undef all for normal operation */ + +/* if defined: count interrupts and ignore this special one: */ +#undef IM_DEBUG_TIMEOUT 50 +/* verbose interrupt: */ +#undef IM_DEBUG_INT +/* verbose queuecommand: */ +#undef IM_DEBUG_CMD +/* verbose queucommand for specific SCSI-device type: */ +#undef IM_DEBUG_CMD_SPEC_DEV +/* verbose device probing */ +#undef IM_DEBUG_PROBE + +/* device type that shall be displayed on syslog (only during debugging): */ +#define IM_DEBUG_CMD_DEVICE TYPE_TAPE + +/* relative addresses of hardware registers on a subsystem */ +#define IM_CMD_REG (shpnt->io_port) /*Command Interface, (4 bytes long) */ +#define IM_ATTN_REG (shpnt->io_port+4) /*Attention (1 byte) */ +#define IM_CTR_REG (shpnt->io_port+5) /*Basic Control (1 byte) */ +#define IM_INTR_REG (shpnt->io_port+6) /*Interrupt Status (1 byte, r/o) */ +#define IM_STAT_REG (shpnt->io_port+7) /*Basic Status (1 byte, read only) */ + +/* basic I/O-port of first adapter */ +#define IM_IO_PORT 0x3540 +/* maximum number of hosts that can be find */ +#define IM_N_IO_PORT 8 + +/*requests going into the upper nibble of the Attention register */ +/*note: the lower nibble specifies the device(0-14), or subsystem(15) */ +#define IM_IMM_CMD 0x10 /*immediate command */ +#define IM_SCB 0x30 /*Subsystem Control Block command */ +#define IM_LONG_SCB 0x40 /*long Subsystem Control Block command */ +#define IM_EOI 0xe0 /*end-of-interrupt request */ + +/*values for bits 7,1,0 of Basic Control reg. (bits 6-2 reserved) */ +#define IM_HW_RESET 0x80 /*hardware reset */ +#define IM_ENABLE_DMA 0x02 /*enable subsystem's busmaster DMA */ +#define IM_ENABLE_INTR 0x01 /*enable interrupts to the system */ + +/*to interpret the upper nibble of Interrupt Status register */ +/*note: the lower nibble specifies the device(0-14), or subsystem(15) */ +#define IM_SCB_CMD_COMPLETED 0x10 +#define IM_SCB_CMD_COMPLETED_WITH_RETRIES 0x50 +#define IM_ADAPTER_HW_FAILURE 0x70 +#define IM_IMMEDIATE_CMD_COMPLETED 0xa0 +#define IM_CMD_COMPLETED_WITH_FAILURE 0xc0 +#define IM_CMD_ERROR 0xe0 +#define IM_SOFTWARE_SEQUENCING_ERROR 0xf0 + +/*to interpret bits 3-0 of Basic Status register (bits 7-4 reserved) */ +#define IM_CMD_REG_FULL 0x08 +#define IM_CMD_REG_EMPTY 0x04 +#define IM_INTR_REQUEST 0x02 +#define IM_BUSY 0x01 + +/*immediate commands (word written into low 2 bytes of command reg) */ +#define IM_RESET_IMM_CMD 0x0400 +#define IM_FEATURE_CTR_IMM_CMD 0x040c +#define IM_DMA_PACING_IMM_CMD 0x040d +#define IM_ASSIGN_IMM_CMD 0x040e +#define IM_ABORT_IMM_CMD 0x040f +#define IM_FORMAT_PREP_IMM_CMD 0x0417 + +/*SCB (Subsystem Control Block) structure */ +struct im_scb + { + unsigned short command; /*command word (read, etc.) */ + unsigned short enable; /*enable word, modifies cmd */ + union + { + unsigned long log_blk_adr; /*block address on SCSI device */ + unsigned char scsi_cmd_length; /*6,10,12, for other scsi cmd */ + } + u1; + unsigned long sys_buf_adr; /*physical system memory adr */ + unsigned long sys_buf_length; /*size of sys mem buffer */ + unsigned long tsb_adr; /*Termination Status Block adr */ + unsigned long scb_chain_adr; /*optional SCB chain address */ + union + { + struct + { + unsigned short count; /*block count, on SCSI device */ + unsigned short length; /*block length, on SCSI device */ + } + blk; + unsigned char scsi_command[12]; /*other scsi command */ + } + u2; + }; + +/*structure scatter-gather element (for list of system memory areas) */ +struct im_sge + { + void *address; + unsigned long byte_length; + }; + +/*values for SCB command word */ +#define IM_NO_SYNCHRONOUS 0x0040 /*flag for any command */ +#define IM_NO_DISCONNECT 0x0080 /*flag for any command */ +#define IM_READ_DATA_CMD 0x1c01 +#define IM_WRITE_DATA_CMD 0x1c02 +#define IM_READ_VERIFY_CMD 0x1c03 +#define IM_WRITE_VERIFY_CMD 0x1c04 +#define IM_REQUEST_SENSE_CMD 0x1c08 +#define IM_READ_CAPACITY_CMD 0x1c09 +#define IM_DEVICE_INQUIRY_CMD 0x1c0b +#define IM_OTHER_SCSI_CMD_CMD 0x241f + +/* unused, but supported, SCB commands */ +#define IM_GET_COMMAND_COMPLETE_STATUS_CMD 0x1c07 /* command status */ +#define IM_GET_POS_INFO_CMD 0x1c0a /* returns neat stuff */ +#define IM_READ_PREFETCH_CMD 0x1c31 /* caching controller only */ +#define IM_FOMAT_UNIT_CMD 0x1c16 /* format unit */ +#define IM_REASSIGN_BLOCK_CMD 0x1c18 /* in case of error */ + +/*values to set bits in the enable word of SCB */ +#define IM_READ_CONTROL 0x8000 +#define IM_REPORT_TSB_ONLY_ON_ERROR 0x4000 +#define IM_RETRY_ENABLE 0x2000 +#define IM_POINTER_TO_LIST 0x1000 +#define IM_SUPRESS_EXCEPTION_SHORT 0x0400 +#define IM_CHAIN_ON_NO_ERROR 0x0001 + +/*TSB (Termination Status Block) structure */ +struct im_tsb + { + unsigned short end_status; + unsigned short reserved1; + unsigned long residual_byte_count; + unsigned long sg_list_element_adr; + unsigned short status_length; + unsigned char dev_status; + unsigned char cmd_status; + unsigned char dev_error; + unsigned char cmd_error; + unsigned short reserved2; + unsigned short reserved3; + unsigned short low_of_last_scb_adr; + unsigned short high_of_last_scb_adr; + }; + +/*subsystem uses interrupt request level 14 */ +#define IM_IRQ 14 + +/*--------------------------------------------------------------------*/ +/* + The model 95 doesn't have a standard activity light. Instead it + has a row of LEDs on the front. We use the last one as the activity + indicator if we think we're on a model 95. I suspect the model id + check will be either too narrow or too general, and some machines + won't have an activity indicator. Oh well... + + The regular PS/2 disk led is turned on/off by bits 6,7 of system + control port. +*/ + +/* LED display-port (actually, last LED on display) */ +#define MOD95_LED_PORT 0x108 +/* system-control-register of PS/2s with diskindicator */ +#define PS2_SYS_CTR 0x92 + +/* The SCSI-ID(!) of the accessed SCSI-device is shown on PS/2-95 machines' LED + displays. ldn is no longer displayed here, because the ldn mapping is now + done dynamically and the ldn <-> pun,lun maps can be looked-up at boottime + or during uptime in /proc/scsi/ibmmca/ in case of trouble, + interest, debugging or just for having fun. The left number gives the + host-adapter number and the right shows the accessed SCSI-ID. */ + +/* use_display is set by the ibmmcascsi=display command line arg */ +static int use_display = 0; +#define PS2_DISK_LED_ON(ad,id) {\ + if( use_display ) { outb((char)(id+48), MOD95_LED_PORT ); \ + outb((char)(ad+48), MOD95_LED_PORT+1); } \ + else outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); \ +} + +#define PS2_DISK_LED_OFF() {\ + if( use_display ) { outb( ' ', MOD95_LED_PORT ); \ + outb(' ', MOD95_LED_PORT+1); } \ + else outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); \ +} + +/*--------------------------------------------------------------------*/ + +/*list of supported subsystems */ +struct subsys_list_struct + { + unsigned short mca_id; + char *description; + }; +struct subsys_list_struct subsys_list[] = +{ + {0x8efc, "IBM Fast SCSI-2 Adapter"}, + {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"}, + {0x8ef8, "IBM Expansion Unit SCSI Controller"}, + {0x8eff, "IBM SCSI Adapter w/Cache"}, + {0x8efe, "IBM SCSI Adapter"}, +}; + +/*for /proc filesystem */ +struct proc_dir_entry proc_scsi_ibmmca = +{ + PROC_SCSI_IBMMCA, 6, "ibmmca", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +/* Max number of logical devices (can be up from 0 to 14). 15 is the address +of the adapter itself. */ +#define MAX_LOG_DEV 15 + +/*local data for a logical device */ +struct logical_device + { + struct im_scb scb; + struct im_tsb tsb; + struct im_sge sge[16]; + Scsi_Cmnd *cmd; /* SCSI-command that is currently in progress */ + + int device_type; /* type of the SCSI-device. See include/scsi/scsi.h + for interpreation of the possible values */ + int block_length; + }; + +/* statistics of the driver during operations (for proc_info) */ +struct Driver_Statistics + { + /* SCSI statistics on the adapter */ + int ldn_access[MAX_LOG_DEV+1]; /* total accesses on a ldn */ + int ldn_read_access[MAX_LOG_DEV+1]; /* total read-access on a ldn */ + int ldn_write_access[MAX_LOG_DEV+1]; /* total write-access on a ldn */ + int total_accesses; /* total accesses on all ldns */ + int total_interrupts; /* total interrupts (should be + same as total_accesses) */ + /* dynamical assignment statistics */ + int total_scsi_devices; /* number of physical pun,lun */ + int dyn_flag; /* flag showing dynamical mode */ + int dynamical_assignments; /* number of remappings of ldns */ + int ldn_assignments[MAX_LOG_DEV+1]; /* number of remappings of each + ldn */ + }; + +/* data structure for each host adapter */ +struct ibmmca_hostdata +{ + /* array of logical devices */ + struct logical_device _ld[MAX_LOG_DEV]; + /* array to convert (pun, lun) into logical device number */ + unsigned char _get_ldn[8][8]; + /*array that contains the information about the physical SCSI-devices + attached to this host adapter */ + unsigned char _get_scsi[8][8]; + /* used only when checking logical devices */ + int _local_checking_phase_flag; + int _got_interrupt; + int _stat_result; + /* reset status (used only when doing reset) */ + int _reset_status; + /* code of the last SCSI command (needed for panic info) */ + int _last_scsi_command; + /* counter that points on next reassignable ldn for dynamical remapping */ + /* The default value is 7, that is the first reassignable number in + the list on startup. */ + int _next_ldn; + /* Statistics for this IBM-SCSI-host */ + struct Driver_Statistics _IBM_DS; +}; + +/* macros to access host data structure */ +#define HOSTDATA(shpnt) ((struct ibmmca_hostdata *) shpnt->hostdata) +#define subsystem_pun (shpnt->this_id) +#define ld (HOSTDATA(shpnt)->_ld) +#define get_ldn (HOSTDATA(shpnt)->_get_ldn) +#define get_scsi (HOSTDATA(shpnt)->_get_scsi) +#define local_checking_phase_flag (HOSTDATA(shpnt)->_local_checking_phase_flag) +#define got_interrupt (HOSTDATA(shpnt)->_got_interrupt) +#define stat_result (HOSTDATA(shpnt)->_stat_result) +#define reset_status (HOSTDATA(shpnt)->_reset_status) +#define last_scsi_command (HOSTDATA(shpnt)->_last_scsi_command) +#define next_ldn (HOSTDATA(shpnt)->_next_ldn) +#define IBM_DS (HOSTDATA(shpnt)->_IBM_DS) + +/* Define a arbitrary number as subsystem-marker-type. This number is, as + described in the SCSI-Standard, not occupied by other device-types. */ +#define TYPE_IBM_SCSI_ADAPTER 0x2F + +/* Define 0xFF for no device type, because this type is not defined within + the SCSI-standard, therefore, it can be used and should not cause any + harm. */ +#define TYPE_NO_DEVICE 0xFF + +/* define medium-changer. If this is not defined previously, define + this type here. */ +#ifndef TYPE_MEDIUM_CHANGER +#define TYPE_MEDIUM_CHANGER 0x08 +#endif + +/* define operations for immediate_assign */ +#define SET_LDN 0 +#define REMOVE_LDN 1 + +/* reset status flag contents */ +#define IM_RESET_NOT_IN_PROGRESS 0 +#define IM_RESET_IN_PROGRESS 1 +#define IM_RESET_FINISHED_OK 2 +#define IM_RESET_FINISHED_FAIL 3 + +/*-----------------------------------------------------------------------*/ + +/* if this is nonzero, ibmmcascsi option has been passed to the kernel */ +static int io_port[IM_MAX_HOSTS] = { 0, 0, 0, 0 }; +static int scsi_id[IM_MAX_HOSTS] = { 7, 7, 7, 7 }; + +/* fill module-parameters only, when this define is present. + (that is kernel >=2.1.0) */ +#ifdef MODULE_PARM +MODULE_PARM(io_port, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); +MODULE_PARM(scsi_id, "1-" __MODULE_STRING(IM_MAX_HOSTS) "i"); +#endif + +/*counter of concurrent disk read/writes, to turn on/off disk led */ +static int disk_rw_in_progress = 0; + +/* host information */ +static int found = 0; +static struct Scsi_Host *hosts[IM_MAX_HOSTS+1] = { NULL }; +/*-----------------------------------------------------------------------*/ + +/*local functions in forward declaration */ +static void interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); +static void issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, + unsigned char attn_reg); +static void internal_done (Scsi_Cmnd * cmd); +static void check_devices (struct Scsi_Host *shpnt); +static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, + unsigned int lun, unsigned int ldn, + unsigned int operation); +static int device_inquiry(struct Scsi_Host *shpnt, int ldn, + unsigned char *buf); +static char ti_p(int value); +static char ti_l(int value); +static int device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, + int device_type); +static struct Scsi_Host *ibmmca_register(Scsi_Host_Template * template, + int port, int id); + +/* local functions needed for proc_info */ +/* +static int ldn_access_load(struct Scsi_Host *shpnt, int ldn); +*/ +static int ldn_access_total_read_write(struct Scsi_Host *shpnt); + +/*--------------------------------------------------------------------*/ + +static void +interrupt_handler (int irq, void *dev_id, struct pt_regs *regs) +{ + int i = 0; + struct Scsi_Host *shpnt; + unsigned int intr_reg; + unsigned int cmd_result; + unsigned int ldn; + + /* search for one adapter-response on shared interrupt */ + do + shpnt = hosts[i++]; + while (shpnt && !(inb(IM_STAT_REG) & IM_INTR_REQUEST)); + + /* return if some other device on this IRQ caused the interrupt */ + if (!shpnt) return; + + /*get command result and logical device */ + intr_reg = inb (IM_INTR_REG); + cmd_result = intr_reg & 0xf0; + ldn = intr_reg & 0x0f; + + /*must wait for attention reg not busy, then send EOI to subsystem */ + while (1) { + cli (); + if (!(inb (IM_STAT_REG) & IM_BUSY)) + break; + sti (); + } + outb (IM_EOI | ldn, IM_ATTN_REG); + sti (); + + /*these should never happen (hw fails, or a local programming bug) */ + if (cmd_result == IM_ADAPTER_HW_FAILURE) + panic ("IBM MCA SCSI: subsystem hardware failure. Last SCSI_CMD=0x%X. \n", + last_scsi_command); + if (cmd_result == IM_CMD_ERROR) + panic ("IBM MCA SCSI: command error. Last SCSI_CMD=0x%X. \n", + last_scsi_command); + if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR) + panic ("IBM MCA SCSI: software sequencing error. Last SCSI_CMD=0x%X. \n", + last_scsi_command); + + /* if no panic appeared, increase the interrupt-counter */ + IBM_DS.total_interrupts++; + + /*only for local checking phase */ + if (local_checking_phase_flag) + { + stat_result = cmd_result; + got_interrupt = 1; + reset_status = IM_RESET_FINISHED_OK; + return; + } + + /*handling of commands coming from upper level of scsi driver */ + else + { + Scsi_Cmnd *cmd; + + /*verify ldn, and may handle rare reset immediate command */ + if (ldn >= MAX_LOG_DEV) + { + if (ldn == 0xf && reset_status == IM_RESET_IN_PROGRESS) + { + if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) + { + reset_status = IM_RESET_FINISHED_FAIL; + } + else + { + /*reset disk led counter, turn off disk led */ + disk_rw_in_progress = 0; + PS2_DISK_LED_OFF (); + reset_status = IM_RESET_FINISHED_OK; + } + return; + } + else + panic ("IBM MCA SCSI: invalid logical device number.\n"); + } + +#ifdef IM_DEBUG_TIMEOUT + { + static int count = 0; + + if (++count == IM_DEBUG_TIMEOUT) { + printk("IBM MCA SCSI: Ignoring interrupt.\n"); + return; + } + } +#endif + + /*if no command structure, just return, else clear cmd */ + cmd = ld[ldn].cmd; + if (!cmd) + return; + ld[ldn].cmd = 0; + +#ifdef IM_DEBUG_INT + printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", + cmd->cmnd[0], intr_reg, + ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, + ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); +#endif + + /*if this is end of media read/write, may turn off PS/2 disk led */ + if ((ld[ldn].device_type!=TYPE_NO_LUN)&& + (ld[ldn].device_type!=TYPE_NO_DEVICE)) + { /* only access this, if there was a valid device addressed */ + switch (cmd->cmnd[0]) + { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + if (--disk_rw_in_progress == 0) + PS2_DISK_LED_OFF (); + } + } + + /*write device status into cmd->result, and call done function */ + if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE) + cmd->result = ld[ldn].tsb.dev_status & 0x1e; + else + cmd->result = 0; + (cmd->scsi_done) (cmd); + } +} + +/*--------------------------------------------------------------------*/ + +static void +issue_cmd (struct Scsi_Host *shpnt, unsigned long cmd_reg, + unsigned char attn_reg) +{ + /*must wait for attention reg not busy */ + while (1) + { + cli (); + if (!(inb (IM_STAT_REG) & IM_BUSY)) + break; + sti (); + } + + /*write registers and enable system interrupts */ + outl (cmd_reg, IM_CMD_REG); + outb (attn_reg, IM_ATTN_REG); + sti (); +} + +/*--------------------------------------------------------------------*/ + +static void +internal_done (Scsi_Cmnd * cmd) +{ + cmd->SCp.Status++; +} + +/*--------------------------------------------------------------------*/ + +static int ibmmca_getinfo (char *buf, int slot, void *dev) +{ + struct Scsi_Host *shpnt = dev; + int len = 0; + + len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun); + len += sprintf (buf + len, "I/O base address: 0x%x\n", IM_CMD_REG); + return len; +} + +/*--------------------------------------------------------------------*/ + +/* SCSI-SCB-command for device_inquiry */ +static int device_inquiry(struct Scsi_Host *shpnt, int ldn, unsigned char *buf) +{ + struct im_scb scb; + struct im_tsb tsb; + int retries; + + for (retries = 0; retries < 3; retries++) + { + /*fill scb with inquiry command */ + scb.command = IM_DEVICE_INQUIRY_CMD; + scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb.sys_buf_adr = virt_to_bus(buf); + scb.sys_buf_length = 255; + scb.tsb_adr = virt_to_bus(&tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt = 0; + issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); + while (!got_interrupt) + barrier (); + + /*if command succesful, break */ + if (stat_result == IM_SCB_CMD_COMPLETED) + break; + } + + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + else + return 1; +} + +/* SCSI-immediate-command for assign. This functions maps/unmaps specific + ldn-numbers on SCSI (PUN,LUN). It is needed for presetting of the + subsystem and for dynamical remapping od ldns. */ +static int immediate_assign(struct Scsi_Host *shpnt, unsigned int pun, + unsigned int lun, unsigned int ldn, + unsigned int operation) +{ + int retries; + unsigned long imm_command; + + for (retries=0; retries<3; retries ++) + { + imm_command = inl(IM_CMD_REG); + imm_command &= (unsigned long)(0xF8000000); /* keep reserved bits */ + imm_command |= (unsigned long)(IM_ASSIGN_IMM_CMD); + imm_command |= (unsigned long)((lun & 7) << 24); + imm_command |= (unsigned long)((operation & 1) << 23); + imm_command |= (unsigned long)((pun & 7) << 20); + imm_command |= (unsigned long)((ldn & 15) << 16); + + got_interrupt = 0; + issue_cmd (shpnt, (unsigned long)(imm_command), IM_IMM_CMD | 0xf); + while (!got_interrupt) + barrier (); + + /*if command succesful, break */ + if (stat_result == IM_IMMEDIATE_CMD_COMPLETED) + break; + } + + if (retries >= 3) + return 0; + else + return 1; +} + +/* type-interpreter for physical device numbers */ +static char ti_p(int value) +{ + switch (value) + { + case TYPE_IBM_SCSI_ADAPTER: return('A'); break; + case TYPE_DISK: return('D'); break; + case TYPE_TAPE: return('T'); break; + case TYPE_PROCESSOR: return('P'); break; + case TYPE_WORM: return('W'); break; + case TYPE_ROM: return('R'); break; + case TYPE_SCANNER: return('S'); break; + case TYPE_MOD: return('M'); break; + case TYPE_MEDIUM_CHANGER: return('C'); break; + case TYPE_NO_LUN: return('+'); break; /* show NO_LUN */ + case TYPE_NO_DEVICE: + default: return('-'); break; + } +} + +/* type-interpreter for logical devices + (A bit stupid, but it was necessary to get the '-' and the Hex-codes + into one type.) */ +static char ti_l(int value) +{ + static char *t_list = "0123456789abcdef"; + return ((value >= 0 && value <= 15)?t_list[value]:'-'); +} + +/* + The following routine probes the SCSI-devices in four steps: + 1. The current ldn -> pun,lun mapping is removed on the SCSI-adapter. + 2. ldn 0 is used to go through all possible combinations of pun,lun and + a device_inquiry is done to fiddle out whether there is a device + responding or not. This physical map is stored in get_scsi[][]. + 3. The 15 available ldns (0-14) are mapped to existing pun,lun. + If there are more devices than ldns, it stops at 14 for the boot + time. Dynamical remapping will be done in ibmmca_queuecommand. + 4. If there are less than 15 valid pun,lun, the remaining ldns are + mapped to NON-existing pun,lun to satisfy the adapter. Information + about pun,lun -> ldn is stored as before in get_ldn[][]. + This method leads to the result, that the SCSI-pun,lun shown to Linux + mid-level- and higher-level-drivers is exactly corresponding to the + physical reality on the SCSI-bus. Therefore, it is possible that users + of older releases of this driver have to rewrite their fstab-file, because + the /dev/sdXXX could have changed due to the right pun,lun report, now. + The assignment of ALL ldns avoids dynamical remapping by the adapter + itself. + */ +static void check_devices (struct Scsi_Host *shpnt) +{ + int id, lun, ldn; + unsigned char buf[256]; + int count_devices = 0; /* local counter for connected device */ + + /* assign default values to certain variables */ + + IBM_DS.dyn_flag = 0; /* normally no need for dynamical ldn management */ + next_ldn = 7; /* next ldn to be assigned is 7, because 0-6 is 'hardwired'*/ + last_scsi_command = 0; /* emptify last SCSI-command storage */ + + /* initialize the very important driver-informational arrays/structs */ + memset (ld, 0, sizeof ld); + memset (get_ldn, TYPE_NO_DEVICE, sizeof get_ldn); /* this is essential ! */ + memset (get_scsi, TYPE_NO_DEVICE, sizeof get_scsi); /* this is essential ! */ + + for (lun=0; lun<=7; lun++) /* mark the adapter at its pun on all luns*/ + { + get_scsi[subsystem_pun][lun] = TYPE_IBM_SCSI_ADAPTER; + get_ldn[subsystem_pun][lun] = MAX_LOG_DEV; /* make sure, the subsystem + ldn is active for all + luns. */ + } + + /* STEP 1: */ + printk("IBM MCA SCSI: Removing current logical SCSI-device mapping."); + for (ldn=0; ldn 0) + { + /* remove mapping */ + get_ldn[id][lun]=TYPE_NO_DEVICE; + immediate_assign(shpnt,0,0,ldn,REMOVE_LDN); + } + else { + get_ldn[id][lun]=ldn; /* map ldn */ + ldn++; + } + } + } + else if (lun == 0) + { + /* map lun == 0, even if no device exists */ + immediate_assign(shpnt,id,lun,ldn,SET_LDN); + get_ldn[id][lun]=ldn; /* map ldn */ + ldn++; + } + } + } + + /* STEP 4: */ + + /* map remaining ldns to non-existing devices */ + for (lun=1; lun<=7 && ldn=MAX_LOG_DEV) + IBM_DS.dyn_flag = 1; /* dynamical assignment is necessary */ + else + IBM_DS.dyn_flag = 0; /* dynamical assignment is not necessary */ + + /* If no SCSI-devices are assigned, return 1 in order to cause message. */ + if (ldn == 0) + printk("IBM MCA SCSI: Warning: No SCSI-devices found/assignable!\n"); + + /* reset the counters for statistics on the current adapter */ + IBM_DS.total_accesses = 0; + IBM_DS.total_interrupts = 0; + IBM_DS.dynamical_assignments = 0; + memset (IBM_DS.ldn_access, 0x0, sizeof (IBM_DS.ldn_access)); + memset (IBM_DS.ldn_read_access, 0x0, sizeof (IBM_DS.ldn_read_access)); + memset (IBM_DS.ldn_write_access, 0x0, sizeof (IBM_DS.ldn_write_access)); + memset (IBM_DS.ldn_assignments, 0x0, sizeof (IBM_DS.ldn_assignments)); + + return; +} + +/*--------------------------------------------------------------------*/ + +static int +device_exists (struct Scsi_Host *shpnt, int ldn, int *block_length, + int device_type) +{ + struct im_scb scb; + struct im_tsb tsb; + unsigned char buf[256]; + int retries; + + /*if device is CD_ROM, assume block size 2048 and return */ + if (device_type == TYPE_ROM) + { + *block_length = 2048; /* (standard blocksize for yellow-/red-book) */ + return 1; + } + + if (device_type == TYPE_WORM) /* CD-burner, WORM, Linux handles this as CD-ROM + therefore, the block_length is also 2048. */ + { + *block_length = 2048; + return 1; + } + + /* if device is disk, use "read capacity" to find its block size */ + if (device_type == TYPE_DISK) + { + for (retries = 0; retries < 3; retries++) + { + /*fill scb with read capacity command */ + scb.command = IM_READ_CAPACITY_CMD; + scb.enable = IM_READ_CONTROL; + scb.sys_buf_adr = virt_to_bus(buf); + scb.sys_buf_length = 8; + scb.tsb_adr = virt_to_bus(&tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt = 0; + issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); + while (!got_interrupt) + barrier (); + + /*if got capacity, get block length and return one device found */ + if (stat_result == IM_SCB_CMD_COMPLETED) + { + *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); + return 1; + } + } + + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + } + + /* if this is a magneto-optical drive, treat it like a harddisk */ + if (device_type == TYPE_MOD) + { + for (retries = 0; retries < 3; retries++) + { + /*fill scb with read capacity command */ + scb.command = IM_READ_CAPACITY_CMD; + scb.enable = IM_READ_CONTROL; + scb.sys_buf_adr = virt_to_bus(buf); + scb.sys_buf_length = 8; + scb.tsb_adr = virt_to_bus(&tsb); + + /*issue scb to passed ldn, and busy wait for interrupt */ + got_interrupt = 0; + issue_cmd (shpnt, virt_to_bus(&scb), IM_SCB | ldn); + while (!got_interrupt) + barrier (); + + /*if got capacity, get block length and return one device found */ + if (stat_result == IM_SCB_CMD_COMPLETED) + { + *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24); + return 1; + } + } + + /*if all three retries failed, return "no device at this ldn" */ + if (retries >= 3) + return 0; + } + + if (device_type == TYPE_TAPE) /* TAPE-device found */ + { + *block_length = 0; /* not in use (setting by mt and mtst in op.) */ + return 1; + } + + if (device_type == TYPE_PROCESSOR) /* HP-Scanners, diverse SCSI-processing units*/ + { + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (device_type == TYPE_SCANNER) /* other SCSI-scanners */ + { + *block_length = 0; /* they set their stuff on drivers */ + return 1; + } + + if (device_type == TYPE_MEDIUM_CHANGER) /* Medium-Changer */ + { + *block_length = 0; /* One never knows, what to expect on a medium + changer device. */ + return 1; + } + + /* Up to now, no SCSI-devices that are known up to kernel 2.1.31 are + ignored! MO-drives are now supported and treated as harddisk. */ + return 0; +} + +/*--------------------------------------------------------------------*/ + +#ifdef CONFIG_SCSI_IBMMCA + +void +ibmmca_scsi_setup (char *str, int *ints) +{ + if ( str && !strcmp( str, "display" ) ) { + use_display = 1; + } else { + if ( str && !strcmp(str, "normal") ) { + /* scan like on a clone */ + ibm_ansi_order = 0; + } + + if ( ints ) { + int i; + for (i = 0; i < IM_MAX_HOSTS && i < ints[0]; i++) { + io_port[i] = ints[i+1]; + } + } + } +} + +#endif + +/*--------------------------------------------------------------------*/ + +int +ibmmca_detect (Scsi_Host_Template * template) +{ + struct Scsi_Host *shpnt; + int port, id, i, list_size, slot, slot_integscsi; + unsigned pos2, pos3; + + /* if this is not MCA machine, return "nothing found" */ + if (!MCA_bus) + return 0; + + /* get interrupt request level */ + if (request_irq (IM_IRQ, interrupt_handler, SA_SHIRQ, "ibmmca", hosts)) + { + printk("IBM MCA SCSI: Unable to get IRQ %d.\n", IM_IRQ); + return 0; + } + + /* if ibmmcascsi setup option was passed to kernel, return "found" */ + for (i = 0; i < IM_MAX_HOSTS; i++) + if (io_port[i] > 0 && scsi_id[i] >= 0 && scsi_id[i] < 8) + { + printk("IBM MCA SCSI: forced detection, io=0x%x, scsi id=%d.\n", + io_port[i], scsi_id[i]); + ibmmca_register(template, io_port[i], scsi_id[i]); + } + if (found) return found; + + /* first look for the SCSI integrated on the motherboard */ + slot_integscsi = MCA_NOTFOUND; + + /* on my 9556, pos2=05, but only port=0x3540 will work, and */ + /* pos' don't match with "IBM PS/2 SCSI Adapter"'s adf file */ + /* someone who knows how to do detection on integrated SCSI */ + /* needs to work this out. */ + /* don't know if this can detect other model's onboard SCSI */ + pos2 = mca_read_stored_pos(MCA_INTEGSCSI, 2); + if (pos2 != 0xff) { + if ((pos2 & 1) == 0) { + port = IM_IO_PORT + ((pos2 & 0x0e) << 2); + } else { + port = IM_IO_PORT; + } + pos3 = mca_read_stored_pos(MCA_INTEGSCSI, 3); + id = (pos3 & 0xe0) >> 5; + + printk("IBM MCA SCSI: probing integrated SCSI at io=0x%x, scsi id=%d.\n", + port, id); + if ((shpnt = ibmmca_register(template, port, id))) { + mca_set_adapter_name(MCA_INTEGSCSI, "PS/2 Integrated SCSI"); + mca_set_adapter_procfn(MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + slot_integscsi = MCA_INTEGSCSI; + } + + } + + /* now look for other adapters */ + list_size = sizeof(subsys_list) / sizeof(struct subsys_list_struct); + for (i = 0; i < list_size; i++) + { + slot = 0; + while (((slot = mca_find_adapter(subsys_list[i].mca_id, slot)) + != MCA_NOTFOUND) && + slot != slot_integscsi) /* skip integ scsi if found earlier */ + { + pos2 = mca_read_stored_pos(slot, 2); + pos3 = mca_read_stored_pos(slot, 3); + port = IM_IO_PORT + ((pos2 & 0x0e) << 2); + id = (pos3 & 0xe0) >> 5; + printk ("IBM MCA SCSI: %s found in slot %d, io=0x%x, scsi id=%d.\n", + subsys_list[i].description, slot + 1, port, id); + if ((shpnt = ibmmca_register(template, port, id))) + { + mca_set_adapter_name (slot, subsys_list[i].description); + mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo, + shpnt); + } + slot++; + } + } + + if (!found) { + free_irq (IM_IRQ, hosts); + printk("IBM MCA SCSI: No adapter attached.\n"); + } + + return found; +} + +static struct Scsi_Host * +ibmmca_register(Scsi_Host_Template * template, int port, int id) +{ + struct Scsi_Host *shpnt; + int i, j; + + /* check I/O region */ + if (check_region(port, IM_N_IO_PORT)) + { + printk("IBM MCA SCSI: Unable to get I/O region 0x%x-0x%x.\n", + port, port + IM_N_IO_PORT); + return NULL; + } + + /* register host */ + shpnt = scsi_register(template, sizeof(struct ibmmca_hostdata)); + if (!shpnt) + { + printk("IBM MCA SCSI: Unable to register host.\n"); + return NULL; + } + + /* request I/O region */ + request_region(port, IM_N_IO_PORT, "ibmmca"); + + hosts[found++] = shpnt; + shpnt->irq = IM_IRQ; + shpnt->io_port = port; + shpnt->n_io_port = IM_N_IO_PORT; + shpnt->this_id = id; + + /* ask the upper layer to scan backwards (ANSI) */ + shpnt->reverse_scan = ibm_ansi_order; + + reset_status = IM_RESET_NOT_IN_PROGRESS; + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + get_ldn[i][j] = MAX_LOG_DEV; + + /* check which logical devices exist */ + local_checking_phase_flag = 1; + check_devices(shpnt); + local_checking_phase_flag = 0; + + /* an ibm mca subsystem has been detected */ + return shpnt; +} + +/*--------------------------------------------------------------------*/ + +int +ibmmca_command (Scsi_Cmnd * cmd) +{ + ibmmca_queuecommand (cmd, internal_done); + cmd->SCp.Status = 0; + while (!cmd->SCp.Status) + barrier (); + return cmd->result; +} + +/*--------------------------------------------------------------------*/ + +int +ibmmca_release(struct Scsi_Host *shpnt) +{ + release_region(shpnt->io_port, shpnt->n_io_port); + if (!(--found)) + free_irq(shpnt->irq, hosts); + return 0; +} + +/*--------------------------------------------------------------------*/ + +/* The following routine is the SCSI command queue. The old edition is + now improved by dynamical reassignment of ldn numbers that are + currently not assigned. The mechanism works in a way, that first + the physical structure is checked. If at a certain pun,lun a device + should be present, the routine proceeds to the ldn check from + get_ldn. An answer of 0xff would show-up, that the aimed device is + currently not assigned any ldn. At this point, the dynamical + remapping algorithm is called. It works in a way, that it goes in + cyclic order through the ldns from 7 to 14. If a ldn is assigned, + it takes 8 dynamical reassignment calls, until a device looses its + ldn again. With this method it is assured, that while doing + intense I/O between up to eight devices, no dynamical remapping is + done there. ldns 0 through 6(!) are left untouched, which means, that + puns 0 through 7(!) on lun=0 are always accessible without remapping. + These ldns are statically assigned by this driver. The subsystem always + occupies at least one pun, therefore 7 ldns (at lun=0) for other devices + are sufficient. (The adapter uses always ldn=15, at whatever pun it is.) */ +int ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +{ + unsigned int ldn; + unsigned int scsi_cmd; + struct im_scb *scb; + struct Scsi_Host *shpnt = cmd->host; + + int current_ldn; + int id,lun; + int target = cmd->target; + + /*if (target,lun) is NO LUN or not existing at all, return error */ + if ((get_scsi[target][cmd->lun] == TYPE_NO_LUN)|| + (get_scsi[target][cmd->lun] == TYPE_NO_DEVICE)) + { + cmd->result = DID_NO_CONNECT << 16; + done (cmd); + return 0; + } + + /*if (target,lun) unassigned, do further checks... */ + ldn = get_ldn[target][cmd->lun]; + if (ldn >= MAX_LOG_DEV) /* on invalid ldn do special stuff */ + { + if (ldn > MAX_LOG_DEV) /* dynamical remapping if ldn unassigned */ + { + current_ldn = next_ldn; /* stop-value for one circle */ + while (ld[next_ldn].cmd) /* search for a occupied, but not in */ + { /* command-processing ldn. */ + next_ldn ++; + if (next_ldn>=MAX_LOG_DEV) + next_ldn = 7; + if (current_ldn == next_ldn) /* One circle done ? */ + { /* no non-processing ldn found */ + printk("IBM MCA SCSI: Cannot assign SCSI-device dynamically!\n"); + printk(" On ldn 7-14 SCSI-commands everywhere in progress.\n"); + printk(" Reporting DID_NO_CONNECT for device (%d,%d).\n", + target, cmd->lun); + cmd->result = DID_NO_CONNECT << 16;/* return no connect*/ + done (cmd); + return 0; + } + } + + /* unmap non-processing ldn */ + for (id=0; id<=7; id ++) + for (lun=0; lun<=7; lun++) + { + if (get_ldn[id][lun] == next_ldn) + { + get_ldn[id][lun] = TYPE_NO_DEVICE; /* unmap entry */ + goto DYN_ASSIGN; /* jump out as fast as possible */ + } + } + +DYN_ASSIGN: + /* unassign found ldn (pun,lun does not matter for remove) */ + immediate_assign(shpnt,0,0,next_ldn,REMOVE_LDN); + /* assign found ldn to aimed pun,lun */ + immediate_assign(shpnt,target,cmd->lun,next_ldn,SET_LDN); + /* map found ldn to pun,lun */ + get_ldn[target][cmd->lun] = next_ldn; + /* change ldn to the right value, that is now next_ldn */ + ldn = next_ldn; + /* set reduced interrupt_handler-mode for checking */ + local_checking_phase_flag = 1; + /* type is known already (from STEP 2) */ + ld[ldn].device_type = get_scsi[target][cmd->lun]; + /* get device information for ld[ldn] */ + if (device_exists (shpnt, ldn, &ld[ldn].block_length, + ld[ldn].device_type)) + { + ld[ldn].cmd = 0; /* To prevent panic set 0, because + devices that were not assigned, + should have nothing in progress. */ + + /* increase assignment counters for statistics in /proc */ + IBM_DS.dynamical_assignments++; + IBM_DS.ldn_assignments[ldn]++; + } + else + /* panic here, because a device, found at boottime has + vanished */ + panic("IBM MCA SCSI: ldn=0x%x, SCSI-device on (%d,%d) vanished!\n", + ldn, target, cmd->lun); + + /* set back to normal interrupt_handling */ + local_checking_phase_flag = 0; + + /* Information on syslog terminal */ + printk("IBM MCA SCSI: ldn=0x%x dynamically reassigned to (%d,%d).\n", + ldn, target, cmd->lun); + + /* increase next_ldn for next dynamical assignment */ + next_ldn ++; + if (next_ldn>=MAX_LOG_DEV) next_ldn = 7; + } + else + { /* wall against Linux accesses to the subsystem adapter */ + cmd->result = DID_NO_CONNECT << 16; + done (cmd); + return 0; + } + } + + /*verify there is no command already in progress for this log dev */ + if (ld[ldn].cmd) + panic ("IBM MCA SCSI: cmd already in progress for this ldn.\n"); + + /*save done in cmd, and save cmd for the interrupt handler */ + cmd->scsi_done = done; + ld[ldn].cmd = cmd; + + /*fill scb information independent of the scsi command */ + scb = &(ld[ldn].scb); + scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR; + scb->tsb_adr = virt_to_bus(&(ld[ldn].tsb)); + if (cmd->use_sg) + { + int i = cmd->use_sg; + struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer; + if (i > 16) + panic ("IBM MCA SCSI: scatter-gather list too long.\n"); + while (--i >= 0) + { + ld[ldn].sge[i].address = (void *) virt_to_bus(sl[i].address); + ld[ldn].sge[i].byte_length = sl[i].length; + } + scb->enable |= IM_POINTER_TO_LIST; + scb->sys_buf_adr = virt_to_bus(&(ld[ldn].sge[0])); + scb->sys_buf_length = cmd->use_sg * sizeof (struct im_sge); + } + else + { + scb->sys_buf_adr = virt_to_bus(cmd->request_buffer); + scb->sys_buf_length = cmd->request_bufflen; + } + + /*fill scb information dependent on scsi command */ + scsi_cmd = cmd->cmnd[0]; + +#ifdef IM_DEBUG_CMD + printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn); +#endif + + /* for specific device debugging: */ +#ifdef IM_DEBUG_CMD_SPEC_DEV + if (ld[ldn].device_type==IM_DEBUG_CMD_DEVICE) + printk("(SCSI-device-type=0x%x) issue scsi cmd=%02x to ldn=%d\n", + ld[ldn].device_type, scsi_cmd, ldn); +#endif + + /* for possible panics store current command */ + last_scsi_command = scsi_cmd; + + /* update statistical info */ + IBM_DS.total_accesses++; + IBM_DS.ldn_access[ldn]++; + + switch (scsi_cmd) + { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + /* statistics for proc_info */ + if ((scsi_cmd == READ_6)||(scsi_cmd == READ_10)||(scsi_cmd == READ_12)) + IBM_DS.ldn_read_access[ldn]++; /* increase READ-access on ldn stat. */ + else if ((scsi_cmd == WRITE_6)||(scsi_cmd == WRITE_10)|| + (scsi_cmd == WRITE_12)) + IBM_DS.ldn_write_access[ldn]++; /* increase write-count on ldn stat.*/ + + /* Distinguish between disk and other devices. Only disks (that are the + most frequently accessed devices) should be supported by the + IBM-SCSI-Subsystem commands. */ + switch (ld[ldn].device_type) + { + case TYPE_DISK: /* for harddisks enter here ... */ + case TYPE_MOD: /* ... try it also for MO-drives (send flames as */ + /* you like, if this won't work.) */ + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) + { + scb->command = IM_READ_DATA_CMD; + scb->enable |= IM_READ_CONTROL; + } + else + { + scb->command = IM_WRITE_DATA_CMD; + } + if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6) + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) | + (((unsigned) cmd->cmnd[2]) << 8) | + ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16); + scb->u2.blk.count = (unsigned) cmd->cmnd[4]; + } + else + { + scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) | + (((unsigned) cmd->cmnd[4]) << 8) | + (((unsigned) cmd->cmnd[3]) << 16) | + (((unsigned) cmd->cmnd[2]) << 24); + scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) | + (((unsigned) cmd->cmnd[7]) << 8); + } + scb->u2.blk.length = ld[ldn].block_length; + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, target); + break; + + /* for other devices, enter here. Other types are not known by + Linux! TYPE_NO_LUN is forbidden as valid device. */ + case TYPE_ROM: + case TYPE_TAPE: + case TYPE_PROCESSOR: + case TYPE_WORM: + case TYPE_SCANNER: + case TYPE_MEDIUM_CHANGER: + + /* If there is a sequential-device, IBM recommends to use + IM_OTHER_SCSI_CMD_CMD instead of subsystem READ/WRITE. + Good/modern CD-ROM-drives are capable of + reading sequential AND random-access. This leads to the problem, + that random-accesses are covered by the subsystem, but + sequentials are not, as like for tape-drives. Therefore, it is + the easiest way to use IM_OTHER_SCSI_CMD_CMD for all read-ops + on CD-ROM-drives in order not to run into timing problems and + to have a stable state. In addition, data-access on CD-ROMs + works faster like that. Strange, but obvious. */ + + scb->command = IM_OTHER_SCSI_CMD_CMD; + if (scsi_cmd == READ_6 || scsi_cmd == READ_10 || + scsi_cmd == READ_12) /* enable READ */ + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + else + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /* assume WRITE */ + + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + + /* Read/write on this non-disk devices is also displayworthy, + so flash-up the LED/display. */ + if (++disk_rw_in_progress == 1) + PS2_DISK_LED_ON (shpnt->host_no, target); + break; + } + break; + case INQUIRY: + scb->command = IM_DEVICE_INQUIRY_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + break; + + case READ_CAPACITY: + scb->command = IM_READ_CAPACITY_CMD; + scb->enable |= IM_READ_CONTROL; + /* the length of system memory buffer must be exactly 8 bytes */ + if (scb->sys_buf_length >= 8) + scb->sys_buf_length = 8; + break; + + /* Commands that need read-only-mode (system <- device): */ + case REQUEST_SENSE: + scb->command = IM_REQUEST_SENSE_CMD; + scb->enable |= IM_READ_CONTROL; + break; + + /* Commands that need write-only-mode (system -> device): */ + case MODE_SELECT: + case MODE_SELECT_10: + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_SUPRESS_EXCEPTION_SHORT; /*Select needs WRITE-enabled*/ + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + break; + + /* For other commands, read-only is useful. Most other commands are + running without an input-data-block. */ + default: + scb->command = IM_OTHER_SCSI_CMD_CMD; + scb->enable |= IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT; + scb->u1.scsi_cmd_length = cmd->cmd_len; + memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len); + break; + } + + /*issue scb command, and return */ + issue_cmd (shpnt, virt_to_bus(scb), IM_SCB | ldn); + return 0; +} + +/*--------------------------------------------------------------------*/ + +int +ibmmca_abort (Scsi_Cmnd * cmd) +{ + /* The code below doesn't work right now, so we tell the upper layer + that we can't abort. This eventually causes a reset. + */ + return SCSI_ABORT_SNOOZE ; + +#if 0 + struct Scsi_host *shpnt = cmd->host; + unsigned int ldn; + void (*saved_done) (Scsi_Cmnd *); + int target = cmd->target; + + /*get logical device number, and disable system interrupts */ + printk ("IBM MCA SCSI: sending abort to device id=%d lun=%d.\n", + target, cmd->lun); + ldn = get_ldn[target][cmd->lun]; + cli (); + + /*if cmd for this ldn has already finished, no need to abort */ + if (!ld[ldn].cmd) + { + sti (); + return SCSI_ABORT_NOT_RUNNING; + } + + /* Clear ld.cmd, save done function, install internal done, + * send abort immediate command (this enables sys. interrupts), + * and wait until the interrupt arrives. + */ + ld[ldn].cmd = 0; + saved_done = cmd->scsi_done; + cmd->scsi_done = internal_done; + cmd->SCp.Status = 0; + issue_cmd (shpnt, T_IMM_CMD, IM_IMM_CMD | ldn); + while (!cmd->SCp.Status) + barrier (); + + /*if abort went well, call saved done, then return success or error */ + if (cmd->result == 0) + { + cmd->result |= DID_ABORT << 16; + saved_done (cmd); + return SCSI_ABORT_SUCCESS; + } + else + return SCSI_ABORT_ERROR; +#endif +} + +/*--------------------------------------------------------------------*/ + +int +ibmmca_reset (Scsi_Cmnd * cmd, unsigned int reset_flags) +{ + struct Scsi_Host *shpnt = cmd->host; + int ticks = IM_RESET_DELAY*HZ; + + if (local_checking_phase_flag) { + printk("IBM MCA SCSI: unable to reset while checking devices.\n"); + return SCSI_RESET_SNOOZE; + } + + /* issue reset immediate command to subsystem, and wait for interrupt */ + printk("IBM MCA SCSI: resetting all devices.\n"); + cli (); + reset_status = IM_RESET_IN_PROGRESS; + issue_cmd (shpnt, IM_RESET_IMM_CMD, IM_IMM_CMD | 0xf); + while (reset_status == IM_RESET_IN_PROGRESS && --ticks) { + udelay(1000000/HZ); + barrier(); + } + /* if reset did not complete, just return an error*/ + if (!ticks) { + printk("IBM MCA SCSI: reset did not complete within %d seconds.\n", + IM_RESET_DELAY); + reset_status = IM_RESET_FINISHED_FAIL; + return SCSI_RESET_ERROR; + } + + /* if reset failed, just return an error */ + if (reset_status == IM_RESET_FINISHED_FAIL) { + printk("IBM MCA SCSI: reset failed.\n"); + return SCSI_RESET_ERROR; + } + + /* so reset finished ok - call outstanding done's, and return success */ + printk ("IBM MCA SCSI: reset completed without error.\n"); + { + int i; + for (i = 0; i < MAX_LOG_DEV; i++) + { + Scsi_Cmnd *cmd = ld[i].cmd; + if (cmd && cmd->scsi_done) + { + ld[i].cmd = 0; + cmd->result = DID_RESET; + (cmd->scsi_done) (cmd); + } + } + } + return SCSI_RESET_SUCCESS; +} + +/*--------------------------------------------------------------------*/ + +int +ibmmca_biosparam (Disk * disk, kdev_t dev, int *info) +{ + info[0] = 64; + info[1] = 32; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + { + info[0] = 128; + info[1] = 63; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + { + info[0] = 255; + info[1] = 63; + info[2] = disk->capacity / (info[0] * info[1]); + if (info[2] >= 1024) + info[2] = 1023; + } + } + return 0; +} + +/* calculate percentage of total accesses on a ldn */ +/* NOT USED. kernel panic on models with no mathco */ +/* even if math emu compiled into the kernel. why? */ +/* +static int ldn_access_load(struct Scsi_Host *shpnt, int ldn) +{ + if (IBM_DS.total_accesses == 0) return (0); + if (IBM_DS.ldn_access[ldn] == 0) return (0); + return((int)(((float)IBM_DS.ldn_access[ldn]/(float)IBM_DS.total_accesses)*(float)100.000)); +} +*/ + +/* calculate total amount of r/w-accesses */ +static int ldn_access_total_read_write(struct Scsi_Host *shpnt) +{ + int a = 0; + int i; + + for (i=0; i<=MAX_LOG_DEV; i++) + a+=IBM_DS.ldn_read_access[i]+IBM_DS.ldn_write_access[i]; + return(a); +} + +/* routine to display info in the proc-fs-structure (a deluxe feature) */ +int ibmmca_proc_info (char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + int len=0; + int i,id; + struct Scsi_Host *shpnt; + + for (i = 0; hosts[i] && hosts[i]->host_no != hostno; i++); + shpnt = hosts[i]; + if (!shpnt) { + len += sprintf(buffer+len, "\nCan't find adapter for host number %d\n", hostno); + return len; + } + + cli(); + + len += sprintf(buffer+len, "\n\tIBM-SCSI-Subsystem-Linux-Driver, Version %s\n\n\n", + IBMMCA_SCSI_DRIVER_VERSION); + len += sprintf(buffer+len, " SCSI Access-Statistics:\n"); +#ifdef CONFIG_SCSI_MULTI_LUN + len += sprintf(buffer+len, " Multiple LUN probing.....: Yes\n"); +#else + len += sprintf(buffer+len, " Multiple LUN probing.....: No\n"); +#endif + len += sprintf(buffer+len, " This Hostnumber..........: %d\n", + hostno); + len += sprintf(buffer+len, " Device Scanning Order....: %s\n", + (ibm_ansi_order) ? "IBM/ANSI":"Industry Standard"); + len += sprintf(buffer+len, " Base I/O-Port............: 0x%x\n", + IM_CMD_REG); + len += sprintf(buffer+len, " (Shared) IRQ.............: %d\n", + IM_IRQ); + len += sprintf(buffer+len, " Total Interrupts.........: %d\n", + IBM_DS.total_interrupts); + len += sprintf(buffer+len, " Total SCSI Accesses......: %d\n", + IBM_DS.total_accesses); + len += sprintf(buffer+len, " Total SCSI READ/WRITE..: %d\n", + ldn_access_total_read_write(shpnt)); + len += sprintf(buffer+len, " Total SCSI other cmds..: %d\n\n", + IBM_DS.total_accesses - ldn_access_total_read_write(shpnt)); + + len += sprintf(buffer+len, " Logical-Device-Number (LDN) Access-Statistics:\n"); + len += sprintf(buffer+len, " LDN | Accesses[ t/T ] | READ | WRITE | ASSIGNMENTS\n"); + len += sprintf(buffer+len, " -----|-----------------|-----------|-----------|--------------\n"); + for (i=0; i<=MAX_LOG_DEV; i++) + len += sprintf(buffer+len, " %2X |%8d/%8d| %8d | %8d | %8d\n", + i, + IBM_DS.ldn_access[i], IBM_DS.total_accesses, + IBM_DS.ldn_read_access[i], + IBM_DS.ldn_write_access[i], IBM_DS.ldn_assignments[i]); + len += sprintf(buffer+len, " -----------------------------------------------------------\n\n"); + + len += sprintf(buffer+len, " Dynamical-LDN-Assignment-Statistics:\n"); + len += sprintf(buffer+len, " Number of physical SCSI-devices..: %d (+ Adapter)\n", + IBM_DS.total_scsi_devices); + len += sprintf(buffer+len, " Dynamical Assignment necessaray..: %s\n", + IBM_DS.dyn_flag ? "Yes" : "No "); + len += sprintf(buffer+len, " Next LDN to be assigned..........: 0x%x\n", + next_ldn); + len += sprintf(buffer+len, " Dynamical assignments done yet...: %d\n", + IBM_DS.dynamical_assignments); + + len += sprintf(buffer+len, "\nCurrent SCSI-Device-Mapping:\n"); + len += sprintf(buffer+len, " Physical SCSI-Device Map Logical SCSI-Device Map\n"); + len += sprintf(buffer+len, " ID\\LUN 0 1 2 3 4 5 6 7 ID\\LUN 0 1 2 3 4 5 6 7\n"); + for (id=0; id<=7; id++) + { + len += sprintf(buffer+len, " %2d %c %c %c %c %c %c %c %c", + id, ti_p(get_scsi[id][0]), ti_p(get_scsi[id][1]), + ti_p(get_scsi[id][2]), ti_p(get_scsi[id][3]), + ti_p(get_scsi[id][4]), ti_p(get_scsi[id][5]), + ti_p(get_scsi[id][6]), ti_p(get_scsi[id][7])); + len += sprintf(buffer+len, " %2d %c %c %c %c %c %c %c %c\n", + id, ti_l(get_ldn[id][0]), ti_l(get_ldn[id][1]), + ti_l(get_ldn[id][2]), ti_l(get_ldn[id][3]), + ti_l(get_ldn[id][4]), ti_l(get_ldn[id][5]), + ti_l(get_ldn[id][6]), ti_l(get_ldn[id][7])); + } + + len += sprintf(buffer+len, "(A = IBM-Subsystem, D = Harddisk, T = Tapedrive, P = Processor, W = WORM,\n"); + len += sprintf(buffer+len, " R = CD-ROM, S = Scanner, M = MO-Drive, C = Medium-Changer, + = unprovided LUN,\n"); + len += sprintf(buffer+len, " - = nothing found)\n\n"); + + *start = buffer + offset; + len -= offset; + if (len > length) + len = length; + + sti(); + + return len; +} + +#ifdef MODULE +/* Eventually this will go into an include file, but this will be later */ +Scsi_Host_Template driver_template = IBMMCA; + +#include "scsi_module.c" +#endif + +/*--------------------------------------------------------------------*/ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/ibmmca.h linux-2.0.35-mca/drivers/scsi/ibmmca.h --- linux/drivers/scsi/ibmmca.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/drivers/scsi/ibmmca.h Sat Aug 8 22:20:49 1998 @@ -0,0 +1,48 @@ +#ifndef _IBMMCA_H +#define _IBMMCA_H + +/* + * Low Level Driver for the IBM Microchannel SCSI Subsystem + */ + +/*services provided to the higher level of Linux SCSI driver */ +int ibmmca_proc_info (char *, char **, off_t, int, int, int); +int ibmmca_detect (Scsi_Host_Template *); +int ibmmca_release (struct Scsi_Host *); +int ibmmca_command (Scsi_Cmnd *); +int ibmmca_queuecommand (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); +int ibmmca_abort (Scsi_Cmnd *); +int ibmmca_reset (Scsi_Cmnd *, unsigned int); +int ibmmca_biosparam (Disk *, kdev_t, int *); + +/*structure for /proc filesystem */ +extern struct proc_dir_entry proc_scsi_ibmmca; + +/*initialization for Scsi_host_template type */ +#define IBMMCA { \ + NULL, /*next*/ \ + NULL, /*usage_count*/ \ + &proc_scsi_ibmmca, /*proc_dir*/ \ + ibmmca_proc_info, /*proc info fn*/ \ + "IBMMCA", /*name*/ \ + ibmmca_detect, /*detect fn*/ \ + ibmmca_release, /*release fn*/ \ + NULL, /*info fn*/ \ + ibmmca_command, /*command fn*/ \ + ibmmca_queuecommand, /*queuecommand fn*/ \ + ibmmca_abort, /*abort fn*/ \ + ibmmca_reset, /*reset fn*/ \ + NULL, /*slave_attach fn*/ \ + ibmmca_biosparam, /*bios fn*/ \ + 16, /*can_queue*/ \ + 7, /*set by detect*/ \ + 16, /*sg_tablesize*/ \ + 1, /*cmd_per_lun*/ \ + 0, /*present*/ \ + 0, /*unchecked_isa_dma*/ \ + ENABLE_CLUSTERING /*use_clustering*/ \ + } + +#endif /* _IBMMCA_H */ + + diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/scsi/scsi.c linux-2.0.35-mca/drivers/scsi/scsi.c --- linux/drivers/scsi/scsi.c Sat Aug 8 21:36:13 1998 +++ linux-2.0.35-mca/drivers/scsi/scsi.c Sat Aug 8 22:20:49 1998 @@ -474,6 +474,10 @@ else { for (channel = 0; channel <= shpnt->max_channel; channel++) { for (dev = 0; dev < shpnt->max_id; ++dev) { +#ifdef CONFIG_MCA + int tmp_dev = dev; /* save it */ + dev = (shpnt->reverse_scan) ? (shpnt->max_id - tmp_dev - 1) : tmp_dev; +#endif if (shpnt->this_id != dev) { /* @@ -492,6 +496,9 @@ break; /* break means don't probe further for luns!=0 */ } /* for lun ends */ } /* if this_id != id ends */ +#ifdef CONFIG_MCA + dev = tmp_dev; +#endif } /* for dev ends */ } /* for channel ends */ } /* if/else hardcoded */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/sound/Config.in linux-2.0.35-mca/drivers/sound/Config.in --- linux/drivers/sound/Config.in Fri Sep 5 23:43:58 1997 +++ linux-2.0.35-mca/drivers/sound/Config.in Sat Aug 8 22:36:21 1998 @@ -1,15 +1,251 @@ -# -# Sound driver configuration -# -#-------- -# There is another config script which is compatible with rest of -# the kernel. It can be activated by running 'make mkscript' in this -# directory. Please note that this is an _experimental_ feature which -# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui). -#-------- -# -$MAKE -C drivers/sound config || exit 1 +bool 'ProAudioSpectrum 16 support' CONFIG_PAS +bool 'Sound Blaster (SB, SBPro, SB16, clones) support' CONFIG_SB +bool 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_ADLIB +bool 'Gravis Ultrasound support' CONFIG_GUS +bool 'MPU-401 support (NOT for SB16)' CONFIG_MPU401 +bool '6850 UART Midi support' CONFIG_UART6850 +bool 'PSS (ECHO-ADI2111) support' CONFIG_PSS +bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 +bool 'GUS MAX support' CONFIG_GUSMAX +bool 'Microsoft Sound System support' CONFIG_MSS +bool 'Ensoniq SoundScape support' CONFIG_SSCAPE +bool 'MediaTrix AudioTrix Pro support' CONFIG_TRIX +bool 'Support for MAD16 and/or Mozart based cards' CONFIG_MAD16 +bool 'Support for Crystal CS4232 based (PnP) cards' CONFIG_CS4232 +bool 'Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers' CONFIG_MAUI +bool '/dev/dsp and /dev/audio support' CONFIG_AUDIO +bool 'MIDI interface support' CONFIG_MIDI +bool 'FM synthesizer (YM3812/OPL-3) support' CONFIG_YM3812 + +if [ "$CONFIG_SB" = "y" ]; then +hex 'I/O base for SB Check from manual of the card' SBC_BASE 220 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster IRQ Check from manual of the card' SBC_IRQ 7 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster DMA 0, 1 or 3' SBC_DMA 1 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'Sound Blaster 16 bit DMA (_REQUIRED_for SB16, Jazz16, SMW) 5, 6 or 7 (use 1 for 8 bit cards)' SB_DMA2 5 +fi + +if [ "$CONFIG_SB" = "y" ]; then +hex 'MPU401 I/O base of SB16, Jazz16 and ES1688 Check from manual of the card' SB_MPU_BASE 0 +fi + +if [ "$CONFIG_SB" = "y" ]; then +int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Use -1 with SB16' SB_MPU_IRQ -1 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 IRQ 3, 4, 5, 7, 9, 10, 11, 12, 14 or 15' PAS_IRQ 10 +fi + +if [ "$CONFIG_PAS" = "y" ]; then +int 'PAS16 DMA 0, 1, 3, 5, 6 or 7' PAS_DMA 3 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +hex 'I/O base for GUS 210, 220, 230, 240, 250 or 260' GUS_BASE 220 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS IRQ 3, 5, 7, 9, 11, 12 or 15' GUS_IRQ 15 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'GUS DMA 1, 3, 5, 6 or 7' GUS_DMA 6 +fi + +if [ "$CONFIG_GUS" = "y" ]; then +int 'Second DMA channel for GUS 1, 3, 5, 6 or 7' GUS_DMA2 -1 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +hex 'I/O base for the 16 bit daughtercard of GUS 530, 604, E80 or F40' GUS16_BASE 530 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS 16 bit daughtercard IRQ 3, 4, 5, 7, or 9' GUS16_IRQ 7 +fi + +if [ "$CONFIG_GUS16" = "y" ]; then +int 'GUS DMA 0, 1 or 3' GUS16_DMA 3 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +hex 'I/O base for MPU401 Check from manual of the card' MPU_BASE 330 +fi + +if [ "$CONFIG_MPU401" = "y" ]; then +int 'MPU401 IRQ Check from manual of the card' MPU_IRQ 9 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +hex 'I/O base for Maui 210, 230, 260, 290, 300, 320, 338 or 330' MAUI_BASE 330 +fi + +if [ "$CONFIG_MAUI" = "y" ]; then +int 'Maui IRQ 5, 9, 12 or 15' MAUI_IRQ 9 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +hex 'I/O base for UART 6850 MIDI port (Unknown)' U6850_BASE 0 +fi + +if [ "$CONFIG_UART6850" = "y" ]; then +int 'UART6850 IRQ (Unknown)' U6850_IRQ -1 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS I/O base 220 or 240' PSS_BASE 220 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS audio I/O base 530, 604, E80 or F40' PSS_MSS_BASE 530 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio IRQ 7, 9, 10 or 11' PSS_MSS_IRQ 11 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS audio DMA 0, 1 or 3' PSS_MSS_DMA 3 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +hex 'PSS MIDI I/O base ' PSS_MPU_BASE 330 +fi + +if [ "$CONFIG_PSS" = "y" ]; then +int 'PSS MIDI IRQ 3, 4, 5, 7 or 9' PSS_MPU_IRQ 9 +fi +if [ "$CONFIG_MSS" = "y" ]; then +hex 'MSS/WSS I/O base 530, 604, E80 or F40' MSS_BASE 530 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS IRQ 7, 9, 10 or 11' MSS_IRQ 11 +fi + +if [ "$CONFIG_MSS" = "y" ]; then +int 'MSS/WSS DMA 0, 1 or 3' MSS_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape MIDI I/O base 320, 330, 340 or 350' SSCAPE_BASE 330 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape MIDI IRQ ' SSCAPE_IRQ 9 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape initialization DMA 0, 1 or 3' SSCAPE_DMA 3 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +hex 'SoundScape audio I/O base 534, 608, E84 or F44' SSCAPE_MSS_BASE 534 +fi + +if [ "$CONFIG_SSCAPE" = "y" ]; then +int 'SoundScape audio IRQ 7, 9, 10 or 11' SSCAPE_MSS_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix audio I/O base 530, 604, E80 or F40' TRIX_BASE 530 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio IRQ 7, 9, 10 or 11' TRIX_IRQ 11 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix audio DMA 0, 1 or 3' TRIX_DMA 0 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix second (duplex) DMA 0, 1 or 3' TRIX_DMA2 3 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix MIDI I/O base 330, 370, 3B0 or 3F0' TRIX_MPU_BASE 330 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix MIDI IRQ 3, 4, 5, 7 or 9' TRIX_MPU_IRQ 9 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +hex 'AudioTrix SB I/O base 220, 210, 230, 240, 250, 260 or 270' TRIX_SB_BASE 220 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB IRQ 3, 4, 5 or 7' TRIX_SB_IRQ 7 +fi + +if [ "$CONFIG_TRIX" = "y" ]; then +int 'AudioTrix SB DMA 1 or 3' TRIX_SB_DMA 1 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 audio I/O base 530, 604, E80 or F40' CS4232_BASE 530 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio IRQ 5, 7, 9, 11, 12 or 15' CS4232_IRQ 11 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 audio DMA 0, 1 or 3' CS4232_DMA 0 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 second (duplex) DMA 0, 1 or 3' CS4232_DMA2 3 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +hex 'CS4232 MIDI I/O base 330, 370, 3B0 or 3F0' CS4232_MPU_BASE 330 +fi + +if [ "$CONFIG_CS4232" = "y" ]; then +int 'CS4232 MIDI IRQ 5, 7, 9, 11, 12 or 15' CS4232_MPU_IRQ 9 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 audio I/O base 530, 604, E80 or F40' MAD16_BASE 530 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio IRQ 7, 9, 10 or 11' MAD16_IRQ 11 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 audio DMA 0, 1 or 3' MAD16_DMA 3 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 second (duplex) DMA 0, 1 or 3' MAD16_DMA2 0 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +hex 'MAD16 MIDI I/O base 300, 310, 320 or 330 (0 disables)' MAD16_MPU_BASE 330 +fi + +if [ "$CONFIG_MAD16" = "y" ]; then +int 'MAD16 MIDI IRQ 5, 7, 9 or 10' MAD16_MPU_IRQ 9 +fi + +if [ "$CONFIG_AUDIO" = "y" ]; then +int 'Audio DMA buffer size 4096, 16384, 32768 or 65536' DSP_BUFFSIZE 65536 +fi +# +$MAKE -C drivers/sound kernelconfig || exit 1 bool 'Additional low level drivers' CONFIG_LOWLEVEL_SOUND if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/drivers/sound/sb_card.c linux-2.0.35-mca/drivers/sound/sb_card.c --- linux/drivers/sound/sb_card.c Mon Aug 5 03:13:53 1996 +++ linux-2.0.35-mca/drivers/sound/sb_card.c Sat Aug 8 22:20:49 1998 @@ -11,7 +11,7 @@ * for more info. */ #include - +#include #include "sound_config.h" @@ -31,6 +31,58 @@ int probe_sb (struct address_info *hw_config) { +#ifdef CONFIG_MCA + /* MCA code added by ZP Gu (zpg@castle.net) */ + if (MCA_bus) { /* no multiple REPLY card probing */ + int slot; + u_char pos2, pos3, pos4; + + slot = mca_find_adapter( 0x5138, 0 ); + if( slot == MCA_NOTFOUND ) { + slot = mca_find_adapter( 0x5137, 0 ); + + if (slot != MCA_NOTFOUND) + mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); + } else { + mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); + } + + if (slot != MCA_NOTFOUND) { + pos2 = mca_read_stored_pos( slot, 2 ); + pos3 = mca_read_stored_pos( slot, 3 ); + pos4 = mca_read_stored_pos( slot, 4 ); + + if (pos2 & 0x4) { /* enabled? */ + static unsigned short irq[] = { 0, 5, 7, 10 }; + /* + static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; + */ + + hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); + hw_config->irq = irq[(pos4 >> 5) & 0x3]; + hw_config->dma = pos3 & 0xf; + /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ + hw_config->dma2 = (pos3 >> 4) & 0x3; + if (hw_config->dma2 == 0) + hw_config->dma2 = hw_config->dma; + else + hw_config->dma2 += 4; + /* + hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; + */ + + printk("SB: Reply MCA SB at slot=%d \ +iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", + slot+1, + hw_config->io_base, hw_config->irq, + hw_config->dma, hw_config->dma2); + } else { + printk ("Reply SB Base I/O address disabled\n"); + } + } + } +#endif + if (check_region (hw_config->io_base, 16)) { printk ("\n\nsb_dsp.c: I/O port %x already in use\n\n", diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/fs/proc/root.c linux-2.0.35-mca/fs/proc/root.c --- linux/fs/proc/root.c Tue Apr 30 06:09:45 1996 +++ linux-2.0.35-mca/fs/proc/root.c Sat Aug 8 22:20:49 1998 @@ -139,6 +139,16 @@ NULL, &proc_root, NULL }; +#ifdef CONFIG_MCA +struct proc_dir_entry proc_mca = { + PROC_MCA, 3, "mca", + S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, + 0, &proc_dir_inode_operations, + NULL, NULL, + NULL, &proc_root, NULL +}; +#endif + struct proc_dir_entry proc_sys_root = { PROC_SYS, 3, "sys", /* inode, name */ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, /* mode, nlink, uid, gid */ @@ -296,6 +306,9 @@ proc_register(&proc_root, &proc_net); proc_register(&proc_root, &proc_scsi); proc_register(&proc_root, &proc_sys_root); +#ifdef CONFIG_MCA + proc_register(&proc_root, &proc_mca); +#endif #ifdef CONFIG_DEBUG_MALLOC proc_register(&proc_root, &(struct proc_dir_entry) { diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/asm-i386/bugs.h linux-2.0.35-mca/include/asm-i386/bugs.h --- linux/include/asm-i386/bugs.h Tue Dec 2 17:18:11 1997 +++ linux-2.0.35-mca/include/asm-i386/bugs.h Sat Aug 8 22:45:02 1998 @@ -15,6 +15,16 @@ #define CONFIG_BUGi386 +#ifdef CONFIG_MCA +static void mca_pentium( char* s, int* ints ) { + /* some Pentiums with MCA bus have this habit of dying after the + FPU test is done. Has something to do with the IRQs being screwed. + I don't have a Pentium to test, so this is the best I can do to fix + the problem. */ + has_mca_pentium = 1; +} +#endif + static void no_halt(char *s, int *ints) { hlt_works_ok = 0; @@ -149,7 +159,13 @@ static void check_bugs(void) { check_tlb(); +#ifdef CONFIG_MCA + if( !has_mca_pentium ) { + check_fpu(); + } +#else check_fpu(); +#endif check_hlt(); check_pentium_f00f(); system_utsname.machine[1] = '0' + x86; diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/asm-i386/processor.h linux-2.0.35-mca/include/asm-i386/processor.h --- linux/include/asm-i386/processor.h Sat Aug 8 21:30:47 1998 +++ linux-2.0.35-mca/include/asm-i386/processor.h Sat Aug 8 22:20:49 1998 @@ -26,14 +26,19 @@ extern char wp_works_ok; /* doesn't work on a 386 */ extern char hlt_works_ok; /* problems on some 486Dx4's and old 386's */ extern int have_cpuid; /* We have a CPUID */ +extern int has_mca_pentium; /* some MCA Pentiums are screwed */ /* * Bus types (default is ISA, but people can check others with these..) - * MCA_bus hardcoded to 0 for now. */ extern int EISA_bus; -#define MCA_bus 0 -#define MCA_bus__is_a_macro /* for versions in ksyms.c */ +extern int MCA_bus; + +/* from system description table in BIOS. Mostly for MCA use, but +others may find it useful. */ +extern unsigned int machine_id; +extern unsigned int machine_submodel_id; +extern unsigned int BIOS_revision; /* * User space process size: 3GB. This is hardcoded into a few places, diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/linux/blk.h linux-2.0.35-mca/include/linux/blk.h --- linux/include/linux/blk.h Sat Aug 8 21:36:19 1998 +++ linux-2.0.35-mca/include/linux/blk.h Sat Aug 8 22:45:02 1998 @@ -107,6 +107,9 @@ void initrd_init(void); #endif +#ifdef CONFIG_BLK_DEV_PS2 +extern int ps2esdi_init(void); +#endif #define RO_IOCTLS(dev,where) \ case BLKROSET: { int __err; if (!suser()) return -EACCES; \ @@ -200,6 +203,14 @@ #define DEVICE_NAME "xt disk" #define DEVICE_REQUEST do_xd_request +#define DEVICE_NR(device) (MINOR(device) >> 6) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == PS2ESDI_MAJOR) + +#define DEVICE_NAME "PS/2 ESDI" +#define DEVICE_REQUEST do_ps2esdi_request #define DEVICE_NR(device) (MINOR(device) >> 6) #define DEVICE_ON(device) #define DEVICE_OFF(device) diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/linux/major.h linux-2.0.35-mca/include/linux/major.h --- linux/include/linux/major.h Sat Aug 8 21:36:20 1998 +++ linux-2.0.35-mca/include/linux/major.h Sat Aug 8 22:20:49 1998 @@ -62,6 +62,7 @@ #define IDE2_MAJOR 33 #define IDE3_MAJOR 34 #define NETLINK_MAJOR 36 +#define PS2ESDI_MAJOR 36 #define IDETAPE_MAJOR 37 #define Z2RAM_MAJOR 37 #define RISCOM8_NORMAL_MAJOR 48 diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/linux/mca.h linux-2.0.35-mca/include/linux/mca.h --- linux/include/linux/mca.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/include/linux/mca.h Sat Aug 8 22:20:49 1998 @@ -0,0 +1,96 @@ +/* + * Header for Microchannel Architecture Bus + * Written by Martin Kolinek, February 1996 +*/ + +#ifndef _LINUX_MCA_H +#define _LINUX_MCA_H + +/* The detection of MCA bus is done in the real mode (using BIOS). + * The information is exported to the protected code, where this + * variable is set to one in case MCA bus was detected. +*/ +extern int MCA_bus; + +/* maximal number of MCA slots - actually, some machines have less, but +they all have sufficient number of POS registers to cover 8. */ +#define MCA_MAX_SLOT_NR 8 + +/* MCA_NOTFOUND is an error condition. The other two indicate + motherboard POS registers contain the adapter. They might be + returned by the mca_find_adapter() function, and can be used as + arguments to mca_read_stored_pos(). I'm not going to allow direct + access to the motherboard registers until we run across an adapter + that requires it. We don't know enough about them to know if it's + safe. + + See Documentation/mca.txt or one of the existing drivers for + more information. +*/ +#define MCA_NOTFOUND (-1) +#define MCA_INTEGSCSI (MCA_MAX_SLOT_NR) +#define MCA_INTEGVIDEO (MCA_MAX_SLOT_NR+1) + +/* max number of adapters, including both slots and various integrated +things. */ +#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+2) + +/* returns the slot of the first enabled adapter matching id. User can +specify a starting slot beyond zero, to deal with detecting multiple +devices. Returns MCA_NOTFOUND if id not found. Also checks the +integrated adapters. */ +extern int mca_find_adapter( int id, int start ); +extern int mca_find_unused_adapter( int id, int start ); + +/* adapter state info - returns 0 if no */ +extern int mca_isadapter( int slot ); +extern int mca_isenabled( int slot ); + +/* gets a byte out of POS register (stored in memory) */ +extern unsigned char mca_read_stored_pos( int slot, int reg ); + +/* + This can be expanded later. Right now, it gives us a way of + getting meaningful information into the MCA_info structure, + so we can have a more interesting /proc/mca. +*/ +extern void mca_set_adapter_name( int slot, char* name ); +extern char* mca_get_adapter_name( int slot ); + +/* + This sets up an information callback for /proc/mca/slot?. The + function is called with the buffer, slot, and device pointer (or + some equally informative context information, or nothing, if you + prefer), and is expected to put useful information into the + buffer. The adapter name, id, and POS registers get printed + before this is called though, so don't do it again. + + This should be called with a NULL procfn when a module + unregisters, thus preventing kernel crashes and other such + nastiness. +*/ +typedef int (*MCA_ProcFn)( char* buf, int slot, void* dev ); +extern void mca_set_adapter_procfn( int slot, MCA_ProcFn, void* dev ); + +/* These routines actually mess with the hardware POS registers. They +temporarily disable the device (and interrupts), so make sure you know +what you're doing if you use them. Furthermore, writing to a POS may +result in two devices trying to share a resource, which in turn can +result in multiple devices sharing memory spaces, IRQs, or even trashing +hardware. YOU HAVE BEEN WARNED. + +You can only access slots with this. Motherboard registers are off +limits. +*/ + +/* read a byte from the specified POS register. */ +extern unsigned char mca_read_pos( int slot, int reg ); + +/* write a byte to the specified POS register. */ +extern void mca_write_pos( int slot, int reg, unsigned char byte ); + +/* Should only be called by the NMI interrupt handler, this will do some +fancy stuff to figure out what might have generated a NMI. */ +extern void mca_handle_nmi( void ); + +#endif /* _LINUX_MCA_H */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/linux/proc_fs.h linux-2.0.35-mca/include/linux/proc_fs.h --- linux/include/linux/proc_fs.h Sat Aug 8 21:36:21 1998 +++ linux-2.0.35-mca/include/linux/proc_fs.h Sat Aug 8 22:47:04 1998 @@ -21,6 +21,7 @@ PROC_VERSION, PROC_CPUINFO, PROC_PCI, + PROC_MCA, PROC_SELF, /* will change inode # */ PROC_NET, PROC_SCSI, @@ -137,6 +138,8 @@ PROC_SCSI_NCR53C8XX, PROC_SCSI_ULTRASTOR, PROC_SCSI_7000FASST, + PROC_SCSI_IBMMCA, + PROC_SCSI_FD_MCS, PROC_SCSI_EATA2X, PROC_SCSI_AM53C974, PROC_SCSI_SSC, @@ -155,6 +158,15 @@ PROC_SCSI_LAST = (PROC_SCSI_FILE + 16) /* won't ever see more than */ }; /* 16 HBAs in one machine */ +enum mca_directory_inos { + PROC_MCA_MACHINE = (PROC_SCSI_LAST+1), + PROC_MCA_REGISTERS, + PROC_MCA_VIDEO, + PROC_MCA_SCSI, + PROC_MCA_SLOT, /* the 8 adapter slots */ + PROC_MCA_LAST = (PROC_MCA_SLOT + 8) +}; + /* Finally, the dynamically allocatable proc entries are reserved: */ #define PROC_DYNAMIC_FIRST 4096 @@ -202,6 +214,7 @@ extern struct proc_dir_entry proc_sys; extern struct proc_dir_entry proc_pid; extern struct proc_dir_entry proc_pid_fd; +extern struct proc_dir_entry proc_mca; extern struct inode_operations proc_scsi_inode_operations; diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/include/linux/ps2esdi.h linux-2.0.35-mca/include/linux/ps2esdi.h --- linux/include/linux/ps2esdi.h Wed Dec 31 19:00:00 1969 +++ linux-2.0.35-mca/include/linux/ps2esdi.h Sat Aug 8 22:20:50 1998 @@ -0,0 +1,98 @@ +#ifndef _PS2ESDI_H_ +#define _PS2ESDI_H_ + +#define NRML_ESDI_ID 0xddff +#define INTG_ESDI_ID 0xdf9f + +#define PRIMARY_IO_BASE 0x3510 +#define ALT_IO_BASE 0x3518 + +#define ESDI_CMD_INT (io_base+0) +#define ESDI_STT_INT (io_base+0) +#define ESDI_CONTROL (io_base+2) +#define ESDI_STATUS (io_base+2) +#define ESDI_ATTN (io_base+3) +#define ESDI_INTRPT (io_base+3) + +#define STATUS_ENABLED 0x01 +#define STATUS_ALTERNATE 0x02 +#define STATUS_BUSY 0x10 +#define STATUS_STAT_AVAIL 0x08 +#define STATUS_INTR 0x01 +#define STATUS_RESET_FAIL 0xea +#define STATUS_CMD_INF 0x04 + +#define CTRL_SOFT_RESET 0xe4 +#define CTRL_HARD_RESET 0x80 +#define CTRL_EOI 0xe2 +#define CTRL_ENABLE_DMA 0x02 +#define CTRL_ENABLE_INTR 0x01 +#define CTRL_DISABLE_INTR 0x00 + +#define ATT_EOI 0x02 + +/* bits of word 0 of configuration status block. more info see p.38 of tech ref */ +#define CONFIG_IS 0x10 /* Invalid Secondary */ +#define CONFIG_ZD 0x08 /* Zero Defect */ +#define CONFIG_SF 0x04 /* Skewed Format */ +#define CONFIG_FR 0x02 /* Removable */ +#define CONFIG_RT 0x01 /* Retries */ + +#define PORT_SYS_A 0x92 +#define PORT_DMA_FN 0x18 +#define PORT_DMA_EX 0x1a + +#define ON (unsigned char)0x40 +#define OFF (unsigned char)~ON +#define LITE_ON outb(inb(PORT_SYS_A) | ON,PORT_SYS_A) +#define LITE_OFF outb((inb(PORT_SYS_A) & OFF),PORT_SYS_A) + +#define FAIL 0 +#define SUCCES 1 + +#define INT_CMD_COMPLETE 0x01 +#define INT_CMD_ECC 0x03 +#define INT_CMD_RETRY 0x05 +#define INT_CMD_FORMAT 0x06 +#define INT_CMD_ECC_RETRY 0x07 +#define INT_CMD_WARNING 0x08 +#define INT_CMD_ABORT 0x09 +#define INT_RESET 0x0A +#define INT_TRANSFER_REQ 0x0B +#define INT_CMD_FAILED 0x0C +#define INT_DMA_ERR 0x0D +#define INT_CMD_BLK_ERR 0x0E +#define INT_ATTN_ERROR 0x0F + +#define DMA_MASK_CHAN 0x90 +#define DMA_UNMASK_CHAN 0xA0 +#define DMA_WRITE_ADDR 0x20 +#define DMA_WRITE_TC 0x40 +#define DMA_WRITE_MODE 0x70 + +#define CMD_GET_DEV_CONFIG 0x09 +#define CMD_READ 0x4601 +#define CMD_WRITE 0x4602 +#define DMA_READ_16 0x4C +#define DMA_WRITE_16 0x44 + + +#define MB 1024*1024 +#define SECT_SIZE 512 + +#define ERROR 1 +#define OK 0 + +#define HDIO_GETGEO 0x0301 + +#define FALSE 0 +#define TRUE (!FALSE) + +struct ps2esdi_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +#endif /* _PS2ESDI_H_ */ diff -u --recursive --new-file -x RCS -x *.orig -x *.rej -x *~ -x .lxdialog/.* -x sound/* linux/init/main.c linux-2.0.35-mca/init/main.c --- linux/init/main.c Sat Aug 8 21:36:22 1998 +++ linux-2.0.35-mca/init/main.c Sat Aug 8 22:20:50 1998 @@ -68,6 +68,9 @@ extern void sock_init(void); extern unsigned long pci_init(unsigned long, unsigned long); extern void sysctl_init(void); +#ifdef CONFIG_MCA +extern long mca_init(long, long); +#endif extern void no_scroll(char *str, int *ints); extern void swap_setup(char *str, int *ints); @@ -98,6 +101,13 @@ extern void eata2x_setup(char *str, int *ints); extern void u14_34f_setup(char *str, int *ints); extern void fdomain_setup(char *str, int *ints); +#ifdef CONFIG_SCSI_IBMMCA +extern void ibmmca_scsi_setup(char *str, int *ints); +#endif +#ifdef CONFIG_SCSI_FD_MCS +extern void fd_mcs_setup(char *str, int *ints); +#endif +extern void scsi_host_order_setup(char *str, int *ints); extern void in2000_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); extern void wd7000_setup(char *str, int *ints); @@ -108,6 +118,10 @@ #ifdef CONFIG_CDU31A extern void cdu31a_setup(char *str, int *ints); #endif CONFIG_CDU31A +#ifdef CONFIG_BLK_DEV_PS2 +extern void ed_setup(char *str, int *ints); +extern void tp720_setup(char *str, int *ints); +#endif CONFIG_BLK_DEV_PS2 #ifdef CONFIG_MCD extern void mcd_setup(char *str, int *ints); #endif CONFIG_MCD @@ -297,6 +311,9 @@ { "no387", no_387 }, { "reboot=", reboot_setup }, #endif +#ifdef CONFIG_MCA + { "mca-pentium", mca_pentium }, +#endif #ifdef CONFIG_INET { "ether=", eth_setup }, #endif @@ -305,6 +322,7 @@ #endif #ifdef CONFIG_SCSI { "max_scsi_luns=", scsi_luns_setup }, + { "scsi-probe=", scsi_host_order_setup }, #endif #ifdef CONFIG_SCSI_ADVANSYS { "advansys=", advansys_setup }, @@ -384,6 +402,18 @@ #ifdef CONFIG_BLK_DEV_FD { "floppy=", floppy_setup }, #endif +#ifdef CONFIG_SCSI_IBMMCA + { "ibmmcascsi=", ibmmca_scsi_setup }, +#endif +#ifdef CONFIG_SCSI_FD_MCS + { "fd_mcs=", fd_mcs_setup }, +#endif +#ifdef CONFIG_BLK_DEV_PS2 + { "ed=", ed_setup }, /* should be phased out eventually */ + { "eda=", ed_setup }, + { "edb=", ed_setup }, + { "tp720", tp720_setup }, +#endif #ifdef CONFIG_CDU31A { "cdu31a=", cdu31a_setup }, #endif CONFIG_CDU31A @@ -619,6 +649,8 @@ { "hdf", 0x2140 }, { "hdg", 0x2200 }, { "hdh", 0x2240 }, + { "eda", 0x2400 }, + { "edb", 0x2440 }, { "sda", 0x0800 }, { "sdb", 0x0810 }, { "sdc", 0x0820 }, @@ -915,6 +947,9 @@ memory_start = console_init(memory_start,memory_end); #ifdef CONFIG_PCI memory_start = pci_init(memory_start,memory_end); +#endif +#ifdef CONFIG_MCA + memory_start = mca_init(memory_start,memory_end); #endif memory_start = kmalloc_init(memory_start,memory_end); sti();