diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/CREDITS linux/CREDITS --- linux.vanilla/CREDITS Sun Dec 6 00:14:35 1998 +++ linux/CREDITS Sun Dec 6 03:14:36 1998 @@ -1245,6 +1245,11 @@ S: B2240 Zandhoven S: Belgium +N: Henning P. Schmiedehausen +E: hps@tanstaafl.de +D: added PCI support to the serial driver +S: Buckenhof, Germany + N: Martin Schulze E: joey@linux.de W: http://home.pages.de/~joey/ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/Configure.help linux/Documentation/Configure.help --- linux.vanilla/Documentation/Configure.help Sun Dec 6 00:14:35 1998 +++ linux/Documentation/Configure.help Fri Dec 11 20:35:53 1998 @@ -79,6 +79,14 @@ arch/i386/math-emu/README. If you are not sure, say Y; apart from resulting in a 45kB bigger kernel, it won't hurt. +Max physical memory +CONFIG_MAX_MEMSIZE + Linux/x86 can use up to ~3.5 gigabytes of physical memory. Default + is maximum 950 megabyte physical memory, this is enough for most + systems. (if you have more than 900MB RAM, see + Documentation/more-than-900MB-RAM.txt how to configure this option. Do + not change this value if you have less than 950MB RAM!) + Normal floppy disk support CONFIG_BLK_DEV_FD If you want to use your floppy disk drive(s) under Linux, say Y. @@ -2055,21 +2063,43 @@ and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. -Tekram DC390(T) (AMD PCscsi) SCSI support +Tekram DC390(T) and Am53/79C974 (PCscsi) SCSI support CONFIG_SCSI_DC390T - This driver supports the Tekram DC390(T) PCI SCSI Hostadapter with - the Am53C974A chip, and perhaps other cards using the same chip. - This driver does _not_ support the DC390W/U/F adaptor with the - NCR/Symbios chips. + This driver supports PCI SCSI host adapters based on the Am53C974A + chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard + PCscsi/PCnet (Am53/79C974) solutions. + Documentation can be found in linux/drivers/scsi/README.tmscsim. + Note that this driver does NOT support Tekram DC390W/U/F, which are + based on NCR/Symbios chips. Use the NCR53C8XX driver for those. + Also note, that there is another generic Am53C974 driver. + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called tmscsim.o. + +Skip support for other Am53/79C974 based SCSI adapters +CONFIG_SCSI_DC390T_NOGENSUPP + Normally, the DC390(T) SCSI driver relies on the DC390 EEPROM to get + initial values for its settings, such as speed, termination, etc. + If it can't find this EEPROM, it will use defaults or the user + supplied boot/module parameters. For details on driver configuration + see linux/drivers/scsi/README.tmscsim. + With this option set, if no EEPROM is found, the driver gives up and + thus only supports Tekram DC390(T) adapters. This can be useful if + you have a DC390(T) and another Am53C974 based adapter, which, for + some reason, you want to drive with the other AM53C974 driver. + If unsure, say N. AM53/79C974 PCI SCSI support CONFIG_SCSI_AM53C974 This is support for the AM53/79C974 SCSI host adapters. Please read drivers/scsi/README.AM53C974 for details. Also, the SCSI-HOWTO, available via FTP (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO, is for you. - Use the native DC390 driver if you've got a Tekram DC390(T) PCI-SCSI - host adapter. + ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO, is for you. + Note that there is another driver for AM53C974 based adapters: The + Tekram DC390(T) driver. + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), GDT SCSI Disk Array Controller support CONFIG_SCSI_GDTH diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/README.DAC960 linux/Documentation/README.DAC960 --- linux.vanilla/Documentation/README.DAC960 Thu Jan 1 01:00:00 1970 +++ linux/Documentation/README.DAC960 Mon Nov 30 07:59:59 1998 @@ -0,0 +1,222 @@ + Mylex DAC960 PCI RAID Controller Driver for Linux + + Version 2.0.0 for Linux 2.0.36 + Version 2.1.0 for Linux 2.1.130 + + BETA TEST RELEASE 3 + + 23 November 1998 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1998 by Leonard N. Zubkoff + + + INTRODUCTION + +Mylex, Inc. designs and manufactures a variety of high performance PCI RAID +controllers based on the Intel i960 processor. Mylex Corporation is located at +34551 Ardenwood Blvd., Fremont, California 94555, USA and can be reached at +510/796-6100 or on the World Wide Web at http://www.mylex.com. Mylex RAID +Technical Support can be reached by electronic mail at support@mylex.com, by +Voice at 510/608-2400, or by FAX at 510/745-7715. Contact information for +offices in Europe and Japan is available on the Web site. + +The latest information on Linux support for DAC960 PCI RAID Controllers, as +well as the most recent release of this driver, will always be available from +my Linux Home Page at URL "http://www.dandelion.com/Linux/". + +Bug reports should be sent via electronic mail to "lnz@dandelion.com". Please +include with the bug report the complete configuration messages reported by the +driver at startup, along with any subsequent system messages relevant to the +controller's operation, and a detailed description of your system's hardware +configuration. + +Please consult the DAC960 documentation for detailed information regarding +installation and configuration of the controllers. This document primarily +provides information specific to the Linux DAC960 support. + + + DRIVER FEATURES + +The DAC960 is supported solely as a high performance RAID controller, not as an +interface to arbitrary SCSI devices. The Linux DAC960 driver operates at the +block device level, the same level as the SCSI and IDE drivers. Unlike other +RAID controllers currently supported on Linux, the DAC960 driver is not +dependent on the SCSI subsystem, and hence avoids all the complexity and +unnecessary code that would be associated with an implementation as a SCSI +driver. The DAC960 driver is designed for as high a performance as possible +with no compromises or extra code for compatibility with lower performance +devices. + +The DAC960 driver is architected to support up to 8 controllers per system. +Each DAC960 controller can support up to 45 disk drives on 3 channels. The +drives installed on a controller are divided into one or more "Drive Groups", +and then each Drive Group is subdivided further into 1 to 32 "Logical Drives". +Each Logical Drive has a specific RAID Level and caching policy associated with +it, and it appears to Linux as a single block device. Logical Drives are +further subdivided into up to 7 partitions through the normal Linux and PC disk +partitioning schemes. Logical Drives are also known as "System Drives", and +Drive Groups are also called "Packs". Both terms are in use in the Mylex +documentation; I have chosen to standardize on the more generic "Logical Drive" +and "Drive Group". + +DAC960 RAID disk devices are named in the style of the Device File System +(DEVFS). The device corresponding to Logical Drive D on Controller C is +referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1 +through /dev/rd/cCdDp7. For example, partition 3 of Logical Drive 5 on +Controller 2 is referred to as /dev/rd/c2d5p3. Note that unlike with SCSI +disks the device names will not change in the event of a disk drive failure. +The DAC960 driver is assigned major numbers 48 - 55 with one major number per +controller. The 8 bits of minor number are divided into 5 bits for the Logical +Drive and 3 bits for the partition. + + + SUPPORTED DAC960 PCI RAID CONTROLLERS + +The following list comprises the supported DAC960 PCI RAID Controllers as of +the date of this document. It is recommended that anyone purchasing a Mylex +PCI RAID Controller not in the following table contact the author beforehand to +verify that it is or will be supported. + +AcceleRAID 250 (DAC960PTL-1) + Uses onboard Symbios SCSI chips on certain motherboards + Also includes one onboard Wide Ultra-2/LVD SCSI channel + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +AcceleRAID 200 (DAC960PTL-0) + Uses onboard Symbios SCSI chips on certain motherboards + Includes no onboard SCSI channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +DAC960PJ 1/2/3 Wide Ultra SCSI-3 Channels + 66MHz Intel i960RD RISC Processor + 4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory + +DAC960PG 1/2/3 Wide Ultra SCSI-3 Channels + 33MHz Intel i960RP RISC Processor + 4MB/8MB ECC EDO Memory + +DAC960PU 1/2/3 Wide Ultra SCSI-3 Channels + Intel i960CF RISC Processor + 2MB/4MB/8MB/16MB/32MB EDRAM or DRAM Memory (max 8MB EDRAM) + +DAC960PD 1/2/3 Wide Fast SCSI-2 Channels + Intel i960CF RISC Processor + 2MB/4MB/8MB/16MB/32MB EDRAM or DRAM Memory (max 8MB EDRAM) + +DAC960PL 1/2/3 Wide Fast SCSI-2 Channels + Intel i960 RISC Processor + 2MB/4MB/8MB/16MB/32MB DRAM Memory + +For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required. +This firmware version is available from http://www.dandelion.com/Linux/ and +will eventually be available from http://www.mylex.com as well. It has been +released by Mylex and is provided with new controllers, but it has not yet +appeared on their support web pages as of the date of this document. + +For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above +required. This firmware version is available from http://www.mylex.com. + +Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers +were delivered with version 2.xx firmware. Version 2.xx firmware is not +supported by this driver and no support is envisioned. Contact Mylex RAID +Technical Support to inquire about upgrading controllers with version 2.xx +firmware to version 3.51-0-04. Upgrading to version 3.xx firmware requires +installation of higher capacity Flash ROM chips, and not all DAC960PD and +DAC960PL controllers can be upgraded. + +Please note that not all SCSI disk drives are suitable for use with DAC960 +controllers, and only particular firmware versions of any given model may +actually function correctly. Similarly, not all motherboards have a BIOS that +properly initializes the AcceleRAID 250, AcceleRAID 200, DAC960PJ, and DAC960PG +because the Intel i960RD/RP is a multi-function device. If in doubt, contact +Mylex RAID Technical Support (support@mylex.com) to verify compatibility. + + + CONTROLLER CONFIGURATION AND STATUS MONITORING + +The DAC960 Online Configuration Utilities are not yet available on Linux but +will hopefully be supported in the future. The AcceleRAID 250, AcceleRAID 200, +DAC960PJ, and DAC960PG controllers can generally be configured using the DAC960 +Configuration Utility included in the controller's BIOS ROM and available via +Alt-R during BIOS initialization. Older DAC960 controllers required the DACCF +utility that runs from a DOS boot disk. + +The status of each DAC960 controller is queried every 7 seconds by the Linux +driver to verify that no problems have been detected, and any changes in status +are reported through appropriate kernel messages. The following log excerpt +details the process of the controller automatically rebuilding onto a spare +drive when a disk drive failed. In this example, 4 drives in a SAF-TE +enclosure were grouped into a Drive Group which was then divided into 3 Logical +Drives configured as RAID-5, RAID-5, and RAID-6. An additional identical drive +was installed as a "Standby" or "Hot Spare" to provide for automatic +rebuilding. The first two messages are the result of the standby drive being +removed, the third message is a result of it being reinstalled, and the +remaining messages are the result of the first drive being removed to force an +automatic rebuild. + +DAC960#0: Physical Drive 0:4 killed because it was removed +DAC960#0: Physical Drive 0:4 is now DEAD +DAC960#0: Physical Drive 0:4 is now STANDBY +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL +DAC960#0: Logical Drive 2 (/dev/rd/c0d2) is now CRITICAL +DAC960#0: Physical Drive 0:0 killed because of timeout on SCSI command +DAC960#0: Physical Drive 0:0 is now DEAD +DAC960#0: Physical Drive 0:0 killed because it was removed +DAC960#0: Physical Drive 0:4 is now WRITE-ONLY +DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 9% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 45% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 0 (/dev/rd/c0d0) 90% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 1 (/dev/rd/c0d1) 28% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 1 (/dev/rd/c0d1) 66% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 2 (/dev/rd/c0d2) 62% completed +DAC960#0: REBUILD IN PROGRESS: Logical Drive 2 (/dev/rd/c0d2) 82% completed +DAC960#0: REBUILD COMPLETED SUCCESSFULLY +DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE +DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE +DAC960#0: Logical Drive 2 (/dev/rd/c0d2) is now ONLINE +DAC960#0: Physical Drive 0:4 is now ONLINE + + + DRIVER INSTALLATION + +This distribution was prepared for Linux kernel version 2.0.36 or 2.1.130. + +To install the DAC960 RAID driver, you may use the following commands, +replacing "/usr/src" with wherever you keep your Linux kernel source tree: + + cd /usr/src + tar -xvzf DAC960-2.0.0-Beta3.tar.gz (or DAC960-2.1.0-Beta3.tar.gz) + mv README.DAC960 DAC960.[ch] linux/drivers/block + patch -p0 < DAC960.patch + cd linux + make config + make depend + make zImage (or bzImage) + +Then install "arch/i386/boot/zImage" or "arch/i386/boot/bzImage" as your +standard kernel, run lilo if appropriate, and reboot. + +To create the necessary devices in /dev, the "make_rd" script included in +"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used. +Also included in this archive are patches to LILO 20 and FDISK v2.8 that add +DAC960 support, along with statically linked executables of LILO and FDISK. +This modified version of LILO will allow booting from a DAC960 controller +and/or mounting the root file system from a DAC960. Unfortunately, installing +directly onto a DAC960 will be problematic until the various Linux distribution +vendors update their installation utilities. + + + DAC960 ANNOUNCEMENTS MAILING LIST + +The DAC960 Announcements Mailing List provides a forum for informing Linux +users of new driver releases and other announcements regarding Linux support +for DAC960 PCI RAID Controllers. To join the mailing list, send a message to +"dac960-announce-request@dandelion.com" with the line "subscribe" in the +message body. diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/Documentation/more-than-900MB-RAM.txt linux/Documentation/more-than-900MB-RAM.txt --- linux.vanilla/Documentation/more-than-900MB-RAM.txt Thu Jan 1 01:00:00 1970 +++ linux/Documentation/more-than-900MB-RAM.txt Fri Dec 11 20:35:53 1998 @@ -0,0 +1,40 @@ + +This document describes how to configure the Linux kernel to +support more than 950MB physical RAM on x86 systems: + +you only have to change the 'Max physical memory in MB' kernel +config option to get everything working. If you have less than +900M RAM, dont touch the default setting, this option buys you +nothing at all! The option is in 'General setup': + + [ ] Kernel math emulation + (1800) Max physical memory in MB + [*] Networking support + [ ] Limit memory to low 16MB + +the unit of CONFIG_MAX_MEMSIZE is 'megabytes', ie. a value of +'1024' means '1024 MBytes'. Unless in 2.1 there is no restriction +on the value of CONFIG_MAX_MEMSIZE! + +IMPORTANT: the value of CONFIG_MAX_MEMSIZE should be about 128M +more than the amount of physical RAM (or 1024 if RAM is less than +900M), because the kernel needs some space for it's own memory +mappings. The kernel enforces this 128M window by clipping away +from the end of phsyical memory if necessary. (in this case that +chunk of physical memory is not used by Linux!) So configure this +option carefully, and look at 'free' output and boot messages +wether all RAM is correctly detected and configured. + +A system with 2G physical memory should use a value of ~2400, a +system with 3.8G memory should use something like 3900. A bit of +experimentation with the limit wont hurt, the kernel needs a ~128M +window for vmalloc() plus PCI space uses up some memory too, thus +physical addresses above FD000000 should rather be kept free. + +if the BIOS does not report correct memory size, use the mem= boot +commandline option to override it. + +feel free to report any problems/suggestions to: + + Ingo Molnar + diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/MAINTAINERS linux/MAINTAINERS --- linux.vanilla/MAINTAINERS Sun Dec 6 00:14:35 1998 +++ linux/MAINTAINERS Fri Dec 11 20:50:36 1998 @@ -165,10 +165,22 @@ S: Maintained CYCLADES ASYNC MUX DRIVER -P: Marcio Saito -M: Marcio Saito +P: Ivan Passos +M: Ivan Passos W: http://www.cyclades.com/ S: Supported + +DC390/AM53C974 SCSI driver +P: Kurt Garloff +M: K.Garloff@ping.de +W: ftp://student.physik.uni-dortmund.de/pub/linux/kernel/dc390/ +S: Maintained + +DAC960 RAID DRIVER +P: Leonard N. Zubkoff +M: Leonard N. Zubkoff +L: linux-raid@vger.rutgers.edu +S: Maintained EATA ISA/EISA/PCI SCSI DRIVER P: Dario Ballabio diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S --- linux.vanilla/arch/i386/boot/setup.S Sun Dec 6 00:14:35 1998 +++ linux/arch/i386/boot/setup.S Sun Dec 6 03:30:21 1998 @@ -482,6 +482,29 @@ out #0x60,al call empty_8042 +! wait until a20 really *is* enabled; it can take a fair amount of +! time on certain systems; Toshiba Tecras are known to have this +! problem. The memory location used here is the int 0x1f vector, +! which should be safe to use; any *unused* memory location < 0xfff0 +! should work here. + +#define TEST_ADDR 0x7c + + push ds + xor ax,ax ! segment 0x0000 + mov ds,ax + dec ax ! segment 0xffff (HMA) + mov gs,ax + mov bx,[TEST_ADDR] ! we want to restore the value later +a20_wait: + inc ax + mov [TEST_ADDR],ax + seg gs + cmp ax,[TEST_ADDR+0x10] + je a20_wait ! loop until no longer aliased + mov [TEST_ADDR],bx ! restore original value + pop ds + ! make sure any possible coprocessor is properly reset.. xor ax,ax diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/config.in linux/arch/i386/config.in --- linux.vanilla/arch/i386/config.in Sun Dec 6 00:14:35 1998 +++ linux/arch/i386/config.in Fri Dec 11 20:35:53 1998 @@ -22,6 +22,7 @@ comment 'General setup' bool 'Kernel math emulation' CONFIG_MATH_EMULATION +int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024 bool 'Networking support' CONFIG_NET bool 'Limit memory to low 16MB' CONFIG_MAX_16M bool 'PCI bios support' CONFIG_PCI diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/defconfig linux/arch/i386/defconfig --- linux.vanilla/arch/i386/defconfig Fri Jul 24 17:28:56 1998 +++ linux/arch/i386/defconfig Fri Dec 11 20:35:53 1998 @@ -55,7 +55,12 @@ # Additional Block Devices # # CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_MD=y +CONFIG_AUTODETECT_RAID=y +CONFIG_MD_LINEAR=y +CONFIG_MD_STRIPED=y +CONFIG_MD_MIRRORING=y +CONFIG_MD_RAID5=y # CONFIG_BLK_DEV_RAM is not set # CONFIG_BLK_DEV_XD is not set # CONFIG_PARIDE is not set diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- linux.vanilla/arch/i386/kernel/head.S Sun Dec 6 00:14:35 1998 +++ linux/arch/i386/kernel/head.S Fri Dec 11 20:35:53 1998 @@ -9,10 +9,14 @@ */ .text +#include #include #include #include #include +#include +#include + #define CL_MAGIC_ADDR 0x90020 #define CL_MAGIC 0xA33F @@ -329,7 +333,7 @@ * sets up a idt with 256 entries pointing to * ignore_int, interrupt gates. It doesn't actually load * idt - that can be done only after paging has been enabled - * and the kernel moved to 0xC0000000. Interrupts + * and the kernel moved to PAGE_OFFSET. Interrupts * are enabled elsewhere, when we can be relatively * sure everything is ok. */ @@ -370,9 +374,9 @@ /* Identity-map the kernel in low 4MB memory for ease of transition */ /* set present bit/user r/w */ movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir) -/* But the real place is at 0xC0000000 */ +/* But the real place is at PAGE_OFFSET */ /* set present bit/user r/w */ - movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+3072 + movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+__USER_PGD_PTRS*4 movl $ SYMBOL_NAME(pg0)+4092,%edi movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */ std @@ -460,7 +464,7 @@ .word 0 idt_descr: .word 256*8-1 # idt contains 256 entries - .long 0xc0000000+SYMBOL_NAME(idt) + .long __PAGE_OFFSET+SYMBOL_NAME(idt) ALIGN .word 0 @@ -470,19 +474,45 @@ #else .word (8+2*NR_TASKS)*8-1 #endif - .long 0xc0000000+SYMBOL_NAME(gdt) + .long __PAGE_OFFSET+SYMBOL_NAME(gdt) /* * This gdt setup gives the kernel a 1GB address space at virtual - * address 0xC0000000 - space enough for expansion, I hope. + * address PAGE_OFFSET - space enough for expansion, I hope. */ + +#define upper_seg(type,dpl,base,limit) \ + ((base) & 0xff000000) | \ + (((base) & 0x00ff0000)>>16) | \ + ((limit) & 0xf0000) | \ + ((dpl)<<13) | \ + (0x00c09000) | \ + ((type)<<8) + +#define lower_seg(type,dpl,base,limit) \ + (((base) & 0x0000ffff)<<16) | \ + ((limit) & 0x0ffff) + +#define x86_seg(type,dpl,base,limit) \ + .long lower_seg(type,dpl,base,limit); \ + .long upper_seg(type,dpl,base,limit) + ENTRY(gdt) .quad 0x0000000000000000 /* NULL descriptor */ .quad 0x0000000000000000 /* not used */ - .quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */ - .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ - .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ - .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ + + /* 0x10 kernel 1GB code at 0xC0000000: */ + x86_seg(0xa,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET) + + /* 0x18 kernel 1GB data at 0xC0000000: */ + x86_seg(0x2,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET) + + /* 0x23 user 3GB code at 0x00000000: */ + x86_seg(0xa,3,0,__PAGE_OFFSET-1) + + /* 0x2b user 3GB data at 0x00000000: */ + x86_seg(0x2,3,0,__PAGE_OFFSET-1) + .quad 0x0000000000000000 /* not used */ .quad 0x0000000000000000 /* not used */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- linux.vanilla/arch/i386/kernel/process.c Sun Jun 21 18:41:24 1998 +++ linux/arch/i386/kernel/process.c Fri Dec 11 20:35:53 1998 @@ -314,10 +314,10 @@ /* Remap the kernel at virtual address zero, as well as offset zero from the kernel segment. This assumes the kernel segment starts at - virtual address 0xc0000000. */ + virtual address PAGE_OFFSET. */ - memcpy (swapper_pg_dir, swapper_pg_dir + 768, - sizeof (swapper_pg_dir [0]) * 256); + memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, + sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS); /* Make sure the first page is mapped to the start of physical memory. It is normally not mapped, to trap kernel NULL pointer dereferences. */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- linux.vanilla/arch/i386/kernel/setup.c Sun Dec 6 00:14:36 1998 +++ linux/arch/i386/kernel/setup.c Fri Dec 11 20:35:53 1998 @@ -167,13 +167,14 @@ #endif /* - * The 1Gig sanity checker. + * The CONFIG_MAX_MEMSIZE sanity checker. */ - if (memory_end > 980*1024*1024) + if (memory_end > (CONFIG_MAX_MEMSIZE-128)*1024*1024) { - printk(KERN_WARNING "Warning only 980Mb will be used.\n"); - memory_end = 980 * 1024 * 1024; + memory_end = (CONFIG_MAX_MEMSIZE-128)*1024*1024; + printk(KERN_WARNING "ONLY %dMB RAM will be used, see Documentation/more-than-900MB-RAM.txt!.\n", CONFIG_MAX_MEMSIZE-128); + udelay(3*1000*1000); } if (!MOUNT_ROOT_RDONLY) diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/arch/i386/mm/init.c linux/arch/i386/mm/init.c --- linux.vanilla/arch/i386/mm/init.c Sun Dec 6 00:14:36 1998 +++ linux/arch/i386/mm/init.c Fri Dec 11 20:35:53 1998 @@ -188,14 +188,14 @@ #endif wp_works_ok = 1; pgd_val(pg_dir[0]) = _PAGE_TABLE | _PAGE_4M | address; - pgd_val(pg_dir[768]) = _PAGE_TABLE | _PAGE_4M | address; + pgd_val(pg_dir[USER_PGD_PTRS]) = _PAGE_TABLE | _PAGE_4M | address; pg_dir++; address += 4*1024*1024; continue; } #endif - /* map the memory at virtual addr 0xC0000000 */ - pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768])); + /* map the memory at virtual addr PAGE_OFFSET */ + pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[USER_PGD_PTRS])); if (!pg_table) { pg_table = (pte_t *) start_mem; start_mem += PAGE_SIZE; @@ -203,7 +203,7 @@ /* also map it temporarily at 0x0000000 for init */ pgd_val(pg_dir[0]) = _PAGE_TABLE | (unsigned long) pg_table; - pgd_val(pg_dir[768]) = _PAGE_TABLE | (unsigned long) pg_table; + pgd_val(pg_dir[USER_PGD_PTRS]) = _PAGE_TABLE | (unsigned long) pg_table; pg_dir++; for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) { if (address < end_mem) diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/Config.in linux/drivers/block/Config.in --- linux.vanilla/drivers/block/Config.in Fri Jul 24 17:28:56 1998 +++ linux/drivers/block/Config.in Sun Dec 6 03:32:15 1998 @@ -53,6 +53,9 @@ bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi tristate 'XT harddisk support' CONFIG_BLK_DEV_XD +if [ "$CONFIG_PCI" = "y" ]; then + bool 'Mylex DAC960 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 +fi tristate 'Parallel port IDE device support' CONFIG_PARIDE if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/Makefile linux/drivers/block/Makefile --- linux.vanilla/drivers/block/Makefile Fri Jul 24 17:28:56 1998 +++ linux/drivers/block/Makefile Sun Dec 6 03:32:15 1998 @@ -118,6 +118,10 @@ endif endif +ifeq ($(CONFIG_BLK_DEV_DAC960),y) +L_OBJS += DAC960.o +endif + ifeq ($(CONFIG_BLK_DEV_MD),y) LX_OBJS += md.o diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/floppy.c linux/drivers/block/floppy.c --- linux.vanilla/drivers/block/floppy.c Sun Dec 6 00:14:36 1998 +++ linux/drivers/block/floppy.c Fri Dec 11 20:47:00 1998 @@ -521,6 +521,8 @@ static unsigned char current_drive = 0; static long current_count_sectors = 0; static unsigned char sector_t; /* sector in track */ +static unsigned char in_sector_offset; /* offset within physical sector, + * expressed in units of 512 bytes */ #ifndef fd_eject #define fd_eject(x) -EINVAL @@ -2056,7 +2058,7 @@ #define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2) #define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1)) -#define CT(x) ((x) | 0x40) +#define CT(x) ((x) | 0xc0) static void setup_format_params(int track) { struct fparm { @@ -2224,7 +2226,7 @@ /* Interrupt handler evaluating the result of the r/w operation */ static void rw_interrupt(void) { - int nr_sectors, ssize, eoc; + int nr_sectors, ssize, eoc, heads; if (!DRS->first_read_date) DRS->first_read_date = jiffies; @@ -2236,23 +2238,32 @@ eoc = 1; else eoc = 0; - nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) * - _floppy->sect + ((R_SECTOR-SECTOR+eoc) << SIZECODE >> 2) - - (sector_t % _floppy->sect) % ssize; + + if(COMMAND & 0x80) + heads = 2; + else + heads = 1; + + nr_sectors = (((R_TRACK-TRACK) * heads + + R_HEAD-HEAD) * SECT_PER_TRACK + + R_SECTOR-SECTOR + eoc) << SIZECODE >> 2; #ifdef FLOPPY_SANITY_CHECK - if (nr_sectors > current_count_sectors + ssize - - (current_count_sectors + sector_t) % ssize + - sector_t % ssize){ + if (nr_sectors / ssize > + (in_sector_offset + current_count_sectors + ssize - 1)/ssize) { DPRINT("long rw: %x instead of %lx\n", nr_sectors, current_count_sectors); printk("rs=%d s=%d\n", R_SECTOR, SECTOR); printk("rh=%d h=%d\n", R_HEAD, HEAD); printk("rt=%d t=%d\n", R_TRACK, TRACK); - printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, - sector_t, ssize); + printk("heads=%d eoc=%d\n", heads, eoc); + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, + sector_t, ssize); + printk("in_sector_offset=%d\n", in_sector_offset); } #endif + + nr_sectors -= in_sector_offset; INFBOUND(nr_sectors,0); SUPBOUND(current_count_sectors, nr_sectors); @@ -2424,6 +2435,32 @@ #endif } +/* work around a bug in pseudo DMA + * (on some FDCs) pseudo DMA does not stop when the CPU stops + * sending data. Hence we need a different way to signal the + * transfer length: We use SECT_PER_TRACK. Unfortunately, this + * does not work with MT, hence we can only transfer one head at + * a time + */ +static void virtualdmabug_workaround(void) { + int hard_sectors, end_sector; + if(CT(COMMAND) == FD_WRITE) { + COMMAND &= ~0x80; /* switch off multiple track mode */ + + hard_sectors = raw_cmd->length >> (7 + SIZECODE); + end_sector = SECTOR + hard_sectors - 1; +#ifdef FLOPPY_SANITY_CHECK + if(end_sector > SECT_PER_TRACK) { + printk("too many sectors %d > %d\n", + end_sector, SECT_PER_TRACK); + return; + } +#endif + SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points + * to end of transfer */ + } +} + /* * Formulate a read/write request. * this routine decides where to load the data (directly to buffer, or to @@ -2495,11 +2532,17 @@ CODE2SIZE; SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE; SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1; + + /* tracksize describes the size which can be filled up with sectors + * of size ssize. + */ tracksize = _floppy->sect - _floppy->sect % ssize; if (tracksize < _floppy->sect){ SECT_PER_TRACK ++; if (tracksize <= sector_t % _floppy->sect) SECTOR--; + + /* if we are beyond tracksize, fill up using smaller sectors */ while (tracksize <= sector_t % _floppy->sect){ while(tracksize + ssize > _floppy->sect){ SIZECODE--; @@ -2509,10 +2552,15 @@ tracksize += ssize; } max_sector = HEAD * _floppy->sect + tracksize; - } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) + } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) { + max_sector = _floppy->sect; + } else if (!HEAD && CT(COMMAND) == FD_WRITE) { + /* for virtual DMA bug workaround */ max_sector = _floppy->sect; + } - aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize; + in_sector_offset = (sector_t % _floppy->sect) % ssize; + aligned_sector_t = sector_t - in_sector_offset; max_size = CURRENT->nr_sectors; if ((raw_cmd->track == buffer_track) && (current_drive == buffer_drive) && @@ -2522,7 +2570,7 @@ copy_buffer(1, max_sector, buffer_max); return 1; } - } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){ + } else if (in_sector_offset || CURRENT->nr_sectors < ssize){ if (CT(COMMAND) == FD_WRITE){ if (sector_t + CURRENT->nr_sectors > ssize && sector_t + CURRENT->nr_sectors < ssize + ssize) @@ -2575,6 +2623,7 @@ indirect, direct, sector_t); return 0; } + virtualdmabug_workaround(); return 2; } } @@ -2588,7 +2637,7 @@ sector_t > buffer_max || sector_t < buffer_min || ((CT(COMMAND) == FD_READ || - (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&& + (!in_sector_offset && CURRENT->nr_sectors >= ssize))&& max_sector > 2 * max_buffer_sectors + buffer_min && max_size + sector_t > 2 * max_buffer_sectors + buffer_min) /* not enough space */){ @@ -2605,7 +2654,7 @@ * is either aligned or the data already in the buffer * (buffer will be overwritten) */ #ifdef FLOPPY_SANITY_CHECK - if (sector_t != aligned_sector_t && buffer_track == -1) + if (in_sector_offset && buffer_track == -1) DPRINT("internal error offset !=0 on write\n"); #endif buffer_track = raw_cmd->track; @@ -2616,7 +2665,7 @@ 2*max_buffer_sectors+buffer_min-aligned_sector_t); /* round up current_count_sectors to get dma xfer size */ - raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t; + raw_cmd->length = in_sector_offset+current_count_sectors; raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1; raw_cmd->length <<= 9; #ifdef FLOPPY_SANITY_CHECK @@ -2678,6 +2727,8 @@ return 0; } #endif + + virtualdmabug_workaround(); return 2; } diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/genhd.c linux/drivers/block/genhd.c --- linux.vanilla/drivers/block/genhd.c Sun Jun 21 18:41:09 1998 +++ linux/drivers/block/genhd.c Sun Dec 6 03:32:15 1998 @@ -51,6 +51,9 @@ extern int chr_dev_init(void); extern int blk_dev_init(void); +#ifdef CONFIG_BLK_DEV_DAC960 +extern void DAC960_Initialize(void); +#endif extern int scsi_dev_init(void); extern int net_dev_init(void); @@ -83,6 +86,19 @@ maj = "hd"; } #endif +#ifdef CONFIG_BLK_DEV_DAC960 + if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7) + { + int controller = hd->major - DAC960_MAJOR; + int partition = minor & ((1 << hd->minor_shift) - 1); + if (partition == 0) + sprintf(buf, "%s/c%dd%d", + maj, controller, minor >> hd->minor_shift); + else sprintf(buf, "%s/c%dd%dp%d", + maj, controller, minor >> hd->minor_shift, partition); + return buf; + } +#endif part = minor & ((1 << hd->minor_shift) - 1); if (part) sprintf(buf, "%s%c%d", maj, unit, part); @@ -93,10 +109,15 @@ static void add_partition (struct gendisk *hd, int minor, int start, int size) { - char buf[8]; + char buf[40]; hd->part[minor].start_sect = start; hd->part[minor].nr_sects = size; - printk(" %s", disk_name(hd, minor, buf)); +#ifdef CONFIG_BLK_DEV_DAC960 + if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7) + printk(" p%d", (minor & ((1 << hd->minor_shift) - 1))); + else +#endif + printk(" %s", disk_name(hd, minor, buf)); } static inline int is_extended_partition(struct partition *p) @@ -641,7 +662,7 @@ { static int first_time = 1; unsigned long first_sector; - char buf[8]; + char buf[40]; if (first_time) printk("Partition check:\n"); @@ -739,6 +760,9 @@ chr_dev_init(); blk_dev_init(); sti(); +#ifdef CONFIG_BLK_DEV_DAC960 + DAC960_Initialize(); +#endif #ifdef CONFIG_SCSI scsi_dev_init(); #endif diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- linux.vanilla/drivers/block/ll_rw_blk.c Fri Jul 24 17:28:57 1998 +++ linux/drivers/block/ll_rw_blk.c Sun Dec 6 03:32:15 1998 @@ -82,6 +82,30 @@ int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, }; /* + * Max number of sectors per request + */ +int * max_sectors[MAX_BLKDEV] = { NULL, NULL, }; + +/* + * Max number of segments per request + */ +int * max_segments[MAX_BLKDEV] = { NULL, NULL, }; + +static inline int get_max_sectors(kdev_t dev) +{ + if (!max_sectors[MAJOR(dev)]) + return MAX_SECTORS; + return max_sectors[MAJOR(dev)][MINOR(dev)]; +} + +static inline int get_max_segments(kdev_t dev) +{ + if (!max_segments[MAJOR(dev)]) + return MAX_SEGMENTS; + return max_segments[MAJOR(dev)][MINOR(dev)]; +} + +/* * remove the plug and let it rip.. */ void unplug_device(void * data) @@ -233,22 +257,24 @@ void add_request(struct blk_dev_struct * dev, struct request * req) { + int major = MAJOR(req->rq_dev); + int minor = MINOR(req->rq_dev); struct request * tmp; short disk_index; - switch (MAJOR(req->rq_dev)) { + switch (major) { case SCSI_DISK_MAJOR: - disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4; + disk_index = (minor & 0x0070) >> 4; if (disk_index < 4) drive_stat_acct(req->cmd, req->nr_sectors, disk_index); break; case IDE0_MAJOR: /* same as HD_MAJOR */ case XT_DISK_MAJOR: - disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6; + disk_index = (minor & 0x0040) >> 6; drive_stat_acct(req->cmd, req->nr_sectors, disk_index); break; case IDE1_MAJOR: - disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2; + disk_index = ((minor & 0x0040) >> 6) + 2; drive_stat_acct(req->cmd, req->nr_sectors, disk_index); default: break; @@ -274,30 +300,40 @@ tmp->next = req; /* for SCSI devices, call request_fn unconditionally */ - if (scsi_blk_major(MAJOR(req->rq_dev))) + if (scsi_blk_major(major)) (dev->request_fn)(); +#ifdef CONFIG_BLK_DEV_DAC960 + if (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) + (dev->request_fn)(); +#endif + sti(); } -#define MAX_SECTORS 244 - -static inline void attempt_merge (struct request *req) +static inline void attempt_merge (struct request *req, + int max_sectors, + int max_segments) { struct request *next = req->next; + int total_segments; if (!next) return; if (req->sector + req->nr_sectors != next->sector) return; - if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS) + if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || + req->nr_sectors + next->nr_sectors > max_sectors) + return; + total_segments = req->nr_segments + next->nr_segments; + if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) + total_segments--; + if (total_segments > max_segments) return; -#if 0 - printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors); -#endif req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; req->nr_sectors += next->nr_sectors; + req->nr_segments = total_segments; next->rq_status = RQ_INACTIVE; req->next = next->next; wake_up (&wait_for_request); @@ -307,7 +343,7 @@ { unsigned int sector, count; struct request * req; - int rw_ahead, max_req; + int rw_ahead, max_req, max_sectors, max_segments; count = bh->b_size >> 9; sector = bh->b_rsector; @@ -390,6 +426,9 @@ /* * Try to coalesce the new request with old requests */ + max_sectors = get_max_sectors(bh->b_rdev); + max_segments = get_max_segments(bh->b_rdev); + cli(); req = blk_dev[major].current_request; if (!req) { @@ -418,25 +457,44 @@ case SCSI_DISK_MAJOR: case SCSI_CDROM_MAJOR: - + case DAC960_MAJOR+0: + case DAC960_MAJOR+1: + case DAC960_MAJOR+2: + case DAC960_MAJOR+3: + case DAC960_MAJOR+4: + case DAC960_MAJOR+5: + case DAC960_MAJOR+6: + case DAC960_MAJOR+7: do { if (req->sem) continue; if (req->cmd != rw) continue; - if (req->nr_sectors >= MAX_SECTORS) + if (req->nr_sectors + count > max_sectors) continue; if (req->rq_dev != bh->b_rdev) continue; /* Can we add it to the end of this request? */ if (req->sector + req->nr_sectors == sector) { + if (req->bhtail->b_data + req->bhtail->b_size + != bh->b_data) { + if (req->nr_segments < max_segments) + req->nr_segments++; + else continue; + } req->bhtail->b_reqnext = bh; req->bhtail = bh; req->nr_sectors += count; /* Can we now merge this req with the next? */ - attempt_merge(req); + attempt_merge(req, max_sectors, max_segments); /* or to the beginning? */ } else if (req->sector - count == sector) { + if (bh->b_data + bh->b_size + != req->bh->b_data) { + if (req->nr_segments < max_segments) + req->nr_segments++; + else continue; + } bh->b_reqnext = req->bh; req->bh = bh; req->buffer = bh->b_data; @@ -470,6 +528,7 @@ req->errors = 0; req->sector = sector; req->nr_sectors = count; + req->nr_segments = 1; req->current_nr_sectors = count; req->buffer = bh->b_data; req->sem = NULL; @@ -634,6 +693,7 @@ req[j]->errors = 0; req[j]->sector = rsector; req[j]->nr_sectors = buffersize >> 9; + req[j]->nr_segments = 1; req[j]->current_nr_sectors = buffersize >> 9; req[j]->buffer = buf; req[j]->sem = &sem; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/Config.in linux/drivers/char/Config.in --- linux.vanilla/drivers/char/Config.in Sun Dec 6 00:14:37 1998 +++ linux/drivers/char/Config.in Sun Dec 6 03:14:36 1998 @@ -5,6 +5,11 @@ comment 'Character devices' tristate 'Standard/generic serial support' CONFIG_SERIAL +if [ "$CONFIG_SERIAL" != "n" ]; then + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' PCI serial support' CONFIG_SERIAL_PCI + fi +fi bool 'Digiboard PC/Xx Support' CONFIG_DIGI tristate 'Cyclades async mux support' CONFIG_CYCLADES if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- linux.vanilla/drivers/char/apm_bios.c Sun Dec 6 00:14:37 1998 +++ linux/drivers/char/apm_bios.c Fri Dec 11 20:35:53 1998 @@ -1167,11 +1167,11 @@ apm_bios_entry.offset = apm_bios_info.offset; apm_bios_entry.segment = APM_CS; set_base(gdt[APM_CS >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4)); + __PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg << 4)); set_base(gdt[APM_CS_16 >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4)); + __PAGE_OFFSET + ((unsigned long)apm_bios_info.cseg_16 << 4)); set_base(gdt[APM_DS >> 3], - 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4)); + __PAGE_OFFSET + ((unsigned long)apm_bios_info.dseg << 4)); if (apm_bios_info.version == 0x100) { set_limit(gdt[APM_CS >> 3], 64 * 1024); set_limit(gdt[APM_CS_16 >> 3], 64 * 1024); diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- linux.vanilla/drivers/char/keyboard.c Sun Jun 21 18:41:10 1998 +++ linux/drivers/char/keyboard.c Sun Dec 6 03:37:59 1998 @@ -374,8 +374,13 @@ mark_bh(CONSOLE_BH); add_keyboard_randomness(scancode); - tty = ttytab[fg_console]; - kbd = kbd_table + fg_console; + tty = ttytab? ttytab[fg_console]: NULL; + if (tty && (!tty->driver_data)) { + /* This is to workaround ugly bug in tty_io.c, which + does not do locking when it should */ + tty = NULL; + } + kbd = kbd_table + fg_console; if ((raw_mode = (kbd->kbdmode == VC_RAW))) { put_queue(scancode); /* we do not return yet, because we want to maintain diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/serial.c linux/drivers/char/serial.c --- linux.vanilla/drivers/char/serial.c Sun Jun 21 18:41:10 1998 +++ linux/drivers/char/serial.c Sun Dec 6 03:14:36 1998 @@ -19,6 +19,9 @@ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. * Bernd Anhäupl 05/17/96. * + * Added Support for PCI serial boards which contain 16x50 Chips + * 31.10.1998 Henning P. Schmiedehausen + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -43,13 +46,18 @@ #include #include +#ifdef CONFIG_SERIAL_PCI +#include +#include +#endif + #include #include #include #include static char *serial_name = "Serial driver"; -static char *serial_version = "4.13"; +static char *serial_version = "4.13p"; DECLARE_TASK_QUEUE(tq_serial); @@ -83,6 +91,10 @@ #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW +#ifdef CONFIG_SERIAL_PCI +# undef SERIAL_DEBUG_PCI +#endif + #define RS_STROBE_TIME (10*HZ) #define RS_ISR_PASS_LIMIT 256 @@ -121,6 +133,14 @@ */ #define BASE_BAUD ( 1843200 / 16 ) +/* + * Well, it is not a 24,756 MHz clock but it is at least a start. + * This PCI board here has a 14,7456 MHz crystal oscillator which is + * eight times as fast as the standard serial clock... + */ + +#define PCI_BAUD ( 14745600 / 16 ) + /* Standard COM flags (except for COM4, because of the 8514 problem) */ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST ) #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF @@ -130,6 +150,29 @@ #define BOCA_FLAGS 0 #define HUB6_FLAGS 0 +#ifdef CONFIG_SERIAL_PCI + +#define PCI_FLAGS (ASYNC_PCI|ASYNC_BOOT_AUTOCONF) + +#ifndef PCI_DEVICE_ID_PLX_SPCOM200 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 +#endif + +/* + * The chips we know about + */ + +#define PCISER_PLX9050 0 /* PLX 9050 local bus bridge as serial card */ +#define PCISER_PCCOM4 1 /* "PC COM PCI Bus 4 port serial Adapter" -- from Alvin Sim */ + +struct pci_serial_boards pci_serial_tbl[] = { + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, "SPCom 200", PCISER_PLX9050, pci_space_0|pci_space_1, 1, 0, 128, PCI_BAUD }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, "PC COM 4", PCISER_PCCOM4, pci_space_0, 4, 8, 128, BASE_BAUD }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +#endif + /* * The following define the access methods for the HUB6 card. All * access is through two ports for all 24 possible chips. The card is @@ -202,10 +245,42 @@ { 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_SERIAL_PCI + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS32 or bigger... */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS33 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS34 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS35 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS36 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS37 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS38 */ + { 0, BASE_BAUD, 0x0, 0, 0 }, /* ttyS39 */ +#endif }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct)) +#ifdef CONFIG_SERIAL_PCI + +/* + * currently you can have up to four PCI serial boards in your + * system. Increase the size of this structure to have more + */ + +struct pci_struct pci_rs_chips[] = { + {0, 0,}, + {0, 0,}, + {0, 0,}, + {0, 0,}, +}; + +#define PCI_NR_BOARDS (sizeof(pci_rs_chips)/sizeof(struct pci_struct)) +#define PCI_NR_PORTS 8 + +#define PCI_PORT_START (NR_PORTS - PCI_NR_PORTS) + +#endif + static struct tty_struct *serial_table[NR_PORTS]; static struct termios *serial_termios[NR_PORTS]; static struct termios *serial_termios_locked[NR_PORTS]; @@ -2478,6 +2553,10 @@ static void show_serial_version(void) { printk(KERN_INFO "%s version %s with", serial_name, serial_version); +#ifdef CONFIG_SERIAL_PCI + printk(" PCI"); +#define SERIAL_OPT +#endif #ifdef CONFIG_HUB6 printk(" HUB-6"); #define SERIAL_OPT @@ -2712,6 +2791,73 @@ restore_flags(flags); } +void display_uart_type(int type) +{ + switch (type) { + case PORT_8250: + printk(" is a 8250\n"); + break; + case PORT_16450: + printk(" is a 16450\n"); + break; + case PORT_16550: + printk(" is a 16550\n"); + break; + case PORT_16550A: + printk(" is a 16550A\n"); + break; + case PORT_16650: + printk(" is a 16650\n"); + break; + default: + printk("\n"); + break; + } +} + +void init_port(struct async_struct *info, int num) +{ + info->magic = SERIAL_MAGIC; + info->line = num; + info->tty = 0; + info->type = PORT_UNKNOWN; + info->custom_divisor = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->tqueue_hangup.routine = do_serial_hangup; + info->tqueue_hangup.data = info; + info->callout_termios =callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info->open_wait = 0; + info->close_wait = 0; + info->delta_msr_wait = 0; + info->icount.cts = info->icount.dsr = + info->icount.rng = info->icount.dcd = 0; + info->next_port = 0; + info->prev_port = 0; + if (info->irq == 2) + info->irq = 9; + + if (info->type == PORT_UNKNOWN) { + if (!(info->flags & ASYNC_BOOT_AUTOCONF)) + return; + autoconfig(info); + if (info->type == PORT_UNKNOWN) + return; + } + printk(KERN_INFO "ttyS%02d%s%s at 0x%04x (irq = %d)", info->line, + (info->flags & ASYNC_FOURPORT) ? " FourPort" : "", + (info->flags & ASYNC_PCI) ? " PCI" : "", + info->port, info->irq); + display_uart_type(info->type); +} + int register_serial(struct serial_struct *req); void unregister_serial(int line); @@ -2722,9 +2868,212 @@ #include }; +#ifdef CONFIG_SERIAL_PCI + +/* + * Query PCI space for known serial boards + * If found, add them to the PCI device space in rs_table[] + * + * Accept a maximum of eight boards + * + */ + +static void probe_serial_pci(void) +{ + u16 vendor, device; + static int pci_index = 0; + unsigned char pci_bus, pci_device_fn; + struct async_struct *pci_boards = &rs_table[PCI_PORT_START]; + unsigned int port_num = 0; + unsigned int card_num = 0; + + u32 device_ioaddr; + u8 device_irq; + + enum pci_spc pci_space = pci_space_0; + unsigned int pci_space_offset = 0; + + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Entered probe_serial_pci()\n"); +#endif + + if (! pcibios_present()) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Leaving probe_serial_pci() (no pcibios)\n"); +#endif + return; + } + +/* + * Start scanning the PCI bus for serial controllers ... + * + */ + + for (;pci_index < 0xff; pci_index++) { + int i = 0; + + if (pcibios_find_class(PCI_CLASS_COMMUNICATION_SERIAL << 8, + pci_index, + &pci_bus, + &pci_device_fn) != PCIBIOS_SUCCESSFUL) + break; /* for (; pci_index ... */ + + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); + + for (i = 0; pci_serial_tbl[i].board_name; i++) { + if (vendor == pci_serial_tbl[i].vendor_id && + device == pci_serial_tbl[i].device_id) + break; /* for(i=0... */ + } + + if (pci_serial_tbl[i].board_name == 0) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Found Board (%x/%x) (not one of us)\n", vendor, device); +#endif + continue; /* Found a serial communication controller but not one we know */ + } + +/* + * At this point we found a serial board which we know + */ + + if(card_num >= PCI_NR_BOARDS) { + printk(KERN_ERR "Already %d boards configured, skipping\n", PCI_NR_BOARDS); + continue; /* for (;pci_index < 0xff */ + } + + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &device_irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &device_ioaddr); + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Device %s at #%x found\n", pci_serial_tbl[i].board_name, device_ioaddr); +#endif + + if (check_region(device_ioaddr, pci_serial_tbl[i].io_size)) { + printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", pci_serial_tbl[i].io_size, device_ioaddr); + continue; /* for (;pci_index < 0xff */ + } + +/* + * Every PCI device brings 128 bytes (at least) of IO-Space with it + * reserve a region for it. It is not exactly necessary as PCI will + * ensure that no other device will be mapped onto this space (LOL) + * but we do it nevertheless so it will show up nicely on + * /proc/ioports -- hps + */ + + if((device_ioaddr & 1) == 0) { +#ifdef SERIAL_DEBUG_PCI + device_ioaddr &= ~0x7f; + printk(KERN_DEBUG "%s has its config registers memory-mapped at #%x (ignoring)\n", + pci_serial_tbl[i].board_name, device_ioaddr); +#endif + continue; /* for (;pci_index < 0xff */ + } + + device_ioaddr &= ~0x7f; /* Mask out the flag bits + * from this register. At least on the PLX9050 + * they're always 0 but this is here nevertheless + * for sanity's sake + */ + + request_region(device_ioaddr, pci_serial_tbl[i].io_size, "serial (PCI Controller)"); + + pci_rs_chips[card_num].start = device_ioaddr; + pci_rs_chips[card_num].type = &pci_serial_tbl[i]; + + +/* + * Every PCI device can bring up to four PCI memory or IO spaces (at + * least according to the documentation I have. So we will now check + * with our config whether this device has one of these spaces and we + * should configure UARTs inside -- hps + */ + + for(; pci_space <= pci_space_3; pci_space <<= 1, pci_space_offset+= 4) { + u32 uart_chip_base; + u32 uart_chip_count; + + if((pci_serial_tbl[i].pci_space & pci_space) == 0) + continue; /* for(;pci_space... */ + + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_2+pci_space_offset, &uart_chip_base); + + if((uart_chip_base & 1) == 0) { +#ifdef SERIAL_DEBUG_PCI + chip_base &= ~0x0f; + printk(KERN_DEBUG "%s has a memory-mapped IO Chip at #%x (ignoring)\n", + pci_serial_tbl[i].board_name, chip_base); +#endif + continue; /* for(;pci_space... */ + } + + uart_chip_base &= ~0x0f; + +/* + * uart_chip_base now points to the IO-Space. + * + * Alvin Sim told me the following thing: + * + * UARTS can be "setserial"d by kernel 2.0.35, but ports needed to be + * manually specified. 4 ports start at 0x6100, in increments of 8 + * addresses. + * + * so there is at least one board out there which can do more than one + * UART in a single PCI config space. My trustworthy SPCom 200 PCI has + * just one UART in one config space. So I added a check for more than + * one chip in a config space -- hps + * + */ + + for(uart_chip_count=0;uart_chip_count < pci_serial_tbl[i].dev_per_space; uart_chip_count++) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "%s has an IO Chip at #%x\n", + pci_serial_tbl[i].board_name, uart_chip_base); +#endif + + if(port_num >= PCI_NR_PORTS) { + printk(KERN_ERR "Already %d ports configured, skipping\n", PCI_NR_PORTS); + break; /* for(;uart_chip_count... */ + } + + if (check_region(uart_chip_base, 8)) { + printk(KERN_ERR "Could not reserve %d bytes of I/O Space at %x\n", 8, uart_chip_base); + break; /* for(;uart_chip_count... */ + } + + request_region(uart_chip_base, 8, "serial (PCI)"); + pci_boards[port_num].port = uart_chip_base; + pci_boards[port_num].irq = device_irq; + pci_boards[port_num].flags = PCI_FLAGS; + pci_boards[port_num].baud_base = pci_serial_tbl[i].baud_base; + + port_num++; + uart_chip_base += pci_serial_tbl[i].dev_spacing; + + } /* for(uart_chip_count... */ + } /* for(pci_space ... */ + + card_num++; + } /* for */ + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n"); +#endif + return; +} + +#endif /* CONFIG_SERIAL_PCI */ + /* * The serial driver boot-time initialization code! */ + int rs_init(void) { int i; @@ -2744,6 +3093,9 @@ } show_serial_version(); +#ifdef CONFIG_SERIAL_PCI + probe_serial_pci(); +#endif /* Initialize the tty_driver structure */ @@ -2795,67 +3147,15 @@ panic("Couldn't register callout driver\n"); for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { - info->magic = SERIAL_MAGIC; - info->line = i; - info->tty = 0; - info->type = PORT_UNKNOWN; - info->custom_divisor = 0; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; - info->callout_termios =callout_driver.init_termios; - info->normal_termios = serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; - info->delta_msr_wait = 0; - info->icount.cts = info->icount.dsr = - info->icount.rng = info->icount.dcd = 0; - info->next_port = 0; - info->prev_port = 0; - if (info->irq == 2) - info->irq = 9; - if (info->type == PORT_UNKNOWN) { - if (!(info->flags & ASYNC_BOOT_AUTOCONF)) - continue; - autoconfig(info); - if (info->type == PORT_UNKNOWN) - continue; - } - printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line, - (info->flags & ASYNC_FOURPORT) ? " FourPort" : "", - info->port, info->irq); - switch (info->type) { - case PORT_8250: - printk(" is a 8250\n"); - break; - case PORT_16450: - printk(" is a 16450\n"); - break; - case PORT_16550: - printk(" is a 16550\n"); - break; - case PORT_16550A: - printk(" is a 16550A\n"); - break; - case PORT_16650: - printk(" is a 16650\n"); - break; - default: - printk("\n"); - break; - } - } + init_port(info, i); + }; + register_symtab(&serial_syms); return 0; } + + /* * register_serial and unregister_serial allows for serial ports to be * configured at run-time, to support PCMCIA modems. @@ -2898,20 +3198,9 @@ printk("register_serial(): autoconfig failed\n"); return -1; } - printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line, + printk(KERN_INFO "ttyS%02d at 0x%04x (irq = %d)", info->line, info->port, info->irq); - switch (info->type) { - case PORT_8250: - printk(" is a 8250\n"); break; - case PORT_16450: - printk(" is a 16450\n"); break; - case PORT_16550: - printk(" is a 16550\n"); break; - case PORT_16550A: - printk(" is a 16550A\n"); break; - default: - printk("\n"); break; - } + display_uart_type(info->type); restore_flags(flags); return info->line; } @@ -2926,7 +3215,7 @@ if (info->tty) tty_hangup(info->tty); info->type = PORT_UNKNOWN; - printk(KERN_INFO "tty%02d unloaded\n", info->line); + printk(KERN_INFO "ttyS%02d unloaded\n", info->line); restore_flags(flags); } @@ -2960,6 +3249,18 @@ if (rs_table[i].type != PORT_UNKNOWN) release_region(rs_table[i].port, 8); } + +#ifdef CONFIG_SERIAL_PCI + for (i = 0; i < PCI_NR_BOARDS; i++) { + if (pci_rs_chips[i].start != 0x0) { +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Releasing %d Bytes at #%x\n", pci_rs_chips[i].type->io_size, pci_rs_chips[i].start); +#endif + release_region(pci_rs_chips[i].start, pci_rs_chips[i].type->io_size); + } + } +#endif + if (tmp_buf) { free_page((unsigned long) tmp_buf); tmp_buf = NULL; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- linux.vanilla/drivers/char/tty_io.c Sun Dec 6 00:14:37 1998 +++ linux/drivers/char/tty_io.c Sun Dec 6 03:37:07 1998 @@ -964,7 +964,13 @@ * Failures after this point use release_mem to clean up, so * there's no need to null out the local pointers. */ - driver->table[idx] = tty; + driver->table[idx] = tty; /* FIXME: this is broken and + probably causes ^D bug. tty->private_date does not (yet) point + to a console, if keypress comes now, await armagedon. + + also, driver->table is accessed from interrupt for vt case, + and this does not look like atomic access at all. */ + if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- linux.vanilla/drivers/net/3c59x.c Sun Dec 6 00:14:39 1998 +++ linux/drivers/net/3c59x.c Sun Dec 6 19:28:57 1998 @@ -15,18 +15,19 @@ */ static char *version = -"3c59x.c:v0.99E 5/12/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; +"3c59x.c:v0.99H 11/17/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; /* "Knobs" that adjust features and parameters. */ /* Set the copy breakpoint for the copy-only-tiny-frames scheme. Setting to > 1512 effectively disables this feature. */ -static const rx_copybreak = 200; +static const int rx_copybreak = 200; /* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ -static const mtu = 1500; +static const int mtu = 1500; /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; /* Put out somewhat more debugging messages. (0: no msg, 1 minimal .. 6). */ +#define vortex_debug debug #ifdef VORTEX_DEBUG static int vortex_debug = VORTEX_DEBUG; #else @@ -37,15 +38,6 @@ debugging. */ static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; -/* Enable the automatic media selection code -- usually set. */ -#define AUTOMEDIA 1 - -/* Allow the use of fragment bus master transfers instead of only - programmed-I/O for Vortex cards. Full-bus-master transfers are always - enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, - the feature may be turned on using 'options'. */ -#define VORTEX_BUS_MASTER - /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT ((400*HZ)/1000) @@ -56,12 +48,12 @@ #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ #include +#include #ifdef MODULE #ifdef MODVERSIONS #include #endif #include -#include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT @@ -70,68 +62,61 @@ #include #include #include -#include +#include #include #include #include #include #include #include +#include +#include +#include +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) #include -#include +#endif #include /* For NR_IRQS only. */ #include #include -#include -#include -#include - -/* Kernel compatibility defines, common to David Hind's PCMCIA package. +/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. This is only in the support-all-kernels source code. */ -#ifndef LINUX_VERSION_CODE -#include /* Redundant above, here for easy clean-up. */ -#endif -#if LINUX_VERSION_CODE < 0x10300 -#define RUN_AT(x) (x) /* What to put in timer->expires. */ -#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#if defined(__alpha) -#error "The Alpha architecture is only support with kernel version 2.0." -#endif -#define virt_to_bus(addr) ((unsigned long)addr) -#define bus_to_virt(addr) ((void*)addr) -#define NR_IRQS 16 -#else /* 1.3.0 and later */ -#define RUN_AT(x) (jiffies + (x)) -#define DEV_ALLOC_SKB(len) dev_alloc_skb(len) -#endif -#if LINUX_VERSION_CODE < 0x20159 -#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE); -#else /* Grrr, unneeded incompatible change. */ -#define DEV_FREE_SKB(skb) dev_kfree_skb(skb); -#endif -#ifdef SA_SHIRQ -#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) -#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) -#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) -#else -#define FREE_IRQ(irqnum, dev) free_irq(irqnum) -#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) -#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) -#endif +#define RUN_AT(x) (jiffies + (x)) -#if (LINUX_VERSION_CODE >= 0x10344) -#define NEW_MULTICAST #include + +#if (LINUX_VERSION_CODE >= 0x20100) +char kernel_version[] = UTS_RELEASE; #else -#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#ifndef __alpha__ +#define ioremap(a,b) \ + (((a)<0x100000) ? (void *)((u_long)(a)) : vremap(a,b)) +#define iounmap(v) \ + do { if ((u_long)(v) > 0x100000) vfree(v); } while (0) +#endif +#endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#define NETSTATS_VER2 #endif - #if LINUX_VERSION_CODE < 0x20138 #define test_and_set_bit(val, addr) set_bit(val, addr) +#define le32_to_cpu(val) (val) +#define cpu_to_le32(val) (val) +#endif +#if LINUX_VERSION_CODE < 0x20155 +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE); +#else /* Grrr, unneeded incompatible change. */ +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb); #endif -#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115) + +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver"); MODULE_PARM(debug, "i"); @@ -141,7 +126,7 @@ MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(compaq_ioaddr, "i"); MODULE_PARM(compaq_irq, "i"); -MODULE_PARM(compaq_prod_id, "i"); +MODULE_PARM(compaq_device_id, "i"); #endif /* Operational parameter that usually are not changed. */ @@ -153,35 +138,11 @@ #define VORTEX_TOTAL_SIZE 0x20 #define BOOMERANG_TOTAL_SIZE 0x40 -#ifdef HAVE_DEVLIST -struct netdev_entry tc59x_drv = -{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL}; -#endif - /* Set iff a MII transceiver on any interface requires mdio preamble. This only set with the original DP83840 on older 3c905 boards, so the extra code size of a per-interface flag is not worthwhile. */ static char mii_preamble_required = 0; -/* Caution! These entries must be consistent. */ -static const int product_ids[] = { - 0x5900, 0x5920, 0x5970, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, - 0x9050, 0x9051, 0x9055, 0x5057, 0 }; -static const char *product_names[] = { - "3c590 Vortex 10Mbps", - "3c592 EISA 10mbps Demon/Vortex", - "3c597 EISA Fast Demon/Vortex", - "3c595 Vortex 100baseTX", - "3c595 Vortex 100baseT4", - "3c595 Vortex 100base-MII", - "3c900 Boomerang 10baseT", - "3c900 Boomerang 10Mbps/Combo", - "3c905 Boomerang 100baseTx", - "3c905 Boomerang 100baseT4", - "3c905B Cyclone 100baseTx", - "3c575", /* Cardbus Boomerang */ -}; - /* Theory of Operation @@ -192,17 +153,19 @@ versions of the FastEtherLink cards. The supported product IDs are 3c590, 3c592, 3c595, 3c597, 3c900, 3c905 -The ISA 3c515 is supported with a separate driver, 3c515.c, included with -the kernel source or available from +The related ISA 3c515 is supported with a separate driver, 3c515.c, included +with the kernel source or available from cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The system BIOS should be set to assign the -PCI INTA signal to an otherwise unused system IRQ line. While it's -physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't -support it. +PCI INTA signal to an otherwise unused system IRQ line. + +The EEPROM settings for media type and forced-full-duplex are observed. +The EEPROM media type should be left at the default "autoselect" unless using +10base2 or AUI connections which cannot be reliably detected. III. Driver operation @@ -213,7 +176,7 @@ The 3c900 "Boomerang" series uses a full-bus-master interface with separate lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, DEC Tulip and Intel Speedo3. The first chip version retains a compatible -programmed-I/O interface that will be removed in the 'B' and subsequent +programmed-I/O interface that has been removed in 'B' and subsequent board revisions. One extension that is advertised in a very large font is that the adapters @@ -231,7 +194,7 @@ single frame. With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme. -Tather than a fixed intermediate receive buffer, this scheme allocates +Rather than a fixed intermediate receive buffer, this scheme allocates full-sized skbuffs as receive buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is chosen to trade-off the memory wasted by passing the full-sized skbuff to the queue layer for all frames vs. the @@ -257,7 +220,66 @@ limit of 4K. */ -#define TCOM_VENDOR_ID 0x10B7 /* 3Com's manufacturer's ID. */ +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int drv_flags, io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, + HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, }; +static struct device *vortex_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int dev_id, int card_idx); +static struct pci_id_info pci_tbl[] = { + {"3c590 Vortex 10Mbps", 0x10B7, 0x5900, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, + {"3c595 Vortex 100baseTx", 0x10B7, 0x5950, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, + {"3c595 Vortex 100baseT4", 0x10B7, 0x5951, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, + {"3c595 Vortex 100base-MII", 0x10B7, 0x5952, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, vortex_probe1}, + {"3Com Vortex", 0x10B7, 0x5900, 0xff00, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, + {"3c900 Boomerang 10baseT", 0x10B7, 0x9000, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, + {"3c900 Boomerang 10Mbps Combo", 0x10B7, 0x9001, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, + {"3c900 Cyclone 10Mbps Combo", 0x10B7, 0x9005, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, + {"3c900B-FL Cyclone 10base-FL", 0x10B7, 0x900A, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, + {"3c905 Boomerang 100baseTx", 0x10B7, 0x9050, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, + {"3c905 Boomerang 100baseT4", 0x10B7, 0x9051, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, + {"3c905B Cyclone 100baseTx", 0x10B7, 0x9055, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY, 128, vortex_probe1}, + {"3c905B-FX Cyclone 100baseFx", 0x10B7, 0x905A, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, + {"3c980 Cyclone", 0x10B7, 0x9800, 0xfff0, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1}, + {"3c575 Boomerang CardBus", 0x10B7, 0x5057, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, + {"3CCFE575 Cyclone CardBus", 0x10B7, 0x5157, 0xffff, + PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS, + 128, vortex_probe1}, + {"3c575 series CardBus (unknown version)", 0x10B7, 0x5057, 0xf0ff, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1}, + {"3Com Boomerang (unknown version)", 0x10B7, 0x9000, 0xff00, + PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG, 64, vortex_probe1}, + {0,}, /* 0 terminated list. */ +}; /* Operational definitions. These are not used by other compilation units and thus are not @@ -286,7 +308,7 @@ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, SetTxThreshold = 18<<11, SetTxStart = 19<<11, StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11,}; + StatsDisable = 22<<11, StopCoax = 23<<11, SetFilterBit = 25<<11,}; /* The SetRxFilter command accepts the following classes: */ enum RxFilter { @@ -326,6 +348,9 @@ NodeAddr01=10, NodeAddr23=11, NodeAddr45=12, DriverTune=13, Checksum=15}; +enum Window2 { /* Window 2. */ + Wn2_ResetOptions=12, +}; enum Window3 { /* Window 3: MAC/config bits. */ Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, }; @@ -393,37 +418,37 @@ enum ChipCaps { CapBusMaster=0x20 }; struct vortex_private { - char devname[8]; /* "ethN" string, also for kernel debug. */ - const char *product_name; - struct device *next_module; - /* The Rx and Tx rings are here to keep them quad-word-aligned. */ + /* The Rx and Tx rings should be quad-word-aligned. */ struct boom_rx_desc rx_ring[RX_RING_SIZE]; struct boom_tx_desc tx_ring[TX_RING_SIZE]; /* The addresses of transmit- and receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct device *next_module; + void *priv_addr; unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - struct enet_statistics stats; + struct net_device_stats stats; struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ /* PCI configuration space information. */ - u8 pci_bus, pci_dev_fn; /* PCI bus location, for power management. */ - u16 pci_device_id; + u8 pci_bus, pci_devfn; /* PCI bus location, for power management. */ + char *cb_fn_base; /* CardBus function status addr space. */ + int chip_id; /* The remainder are related to chip state, mostly media selection. */ - int in_interrupt; + unsigned long in_interrupt; struct timer_list timer; /* Media selection timer. */ int options; /* User-settable misc. driver options. */ - unsigned int - media_override:3, /* Passed-in media type. */ - default_media:3, /* Read from the EEPROM/Wn3_Config. */ - full_duplex:1, autoselect:1, - bus_master:1, /* Vortex can only do a fragment bus-m. */ - full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */ - hw_csums:1, /* Has hardware checksums. */ - tx_full:1; + unsigned int media_override:3, /* Passed-in media type. */ + default_media:4, /* Read from the EEPROM/Wn3_Config. */ + full_duplex:1, force_fd:1, autoselect:1, + bus_master:1, /* Vortex can only do a fragment bus-m. */ + full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */ + hw_csums:1, /* Has hardware checksums. */ + tx_full:1; u16 status_enable; + u16 intr_enable; u16 available_media; /* From Wn3_Options. */ u16 capabilities, info1, info2; /* Various, from EEPROM. */ u16 advertising; /* NWay media advertisement */ @@ -458,61 +483,39 @@ { "Default", 0, 0xFF, XCVR_10baseT, 10000}, }; -static int vortex_scan(struct device *dev); -static struct device *vortex_found_device(struct device *dev, int ioaddr, - int irq, int device_id, - int options, int card_idx); -static int vortex_probe1(struct device *dev); -static int vortex_open(struct device *dev); -static void mdio_sync(int ioaddr, int bits); -static int mdio_read(int ioaddr, int phy_id, int location); -#ifdef HAVE_PRIVATE_IOCTL -static void mdio_write(int ioaddr, int phy_id, int location, int value); +#ifndef CARDBUS +static int vortex_scan(struct device *dev, struct pci_id_info pci_tbl[]); #endif +static int vortex_open(struct device *dev); +static void mdio_sync(long ioaddr, int bits); +static int mdio_read(long ioaddr, int phy_id, int location); +static void mdio_write(long ioaddr, int phy_id, int location, int value); static void vortex_timer(unsigned long arg); static int vortex_start_xmit(struct sk_buff *skb, struct device *dev); static int boomerang_start_xmit(struct sk_buff *skb, struct device *dev); static int vortex_rx(struct device *dev); static int boomerang_rx(struct device *dev); -static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); +static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int vortex_close(struct device *dev); -static void update_stats(int addr, struct device *dev); -static struct enet_statistics *vortex_get_stats(struct device *dev); +static void update_stats(long ioaddr, struct device *dev); +static struct net_device_stats *vortex_get_stats(struct device *dev); static void set_rx_mode(struct device *dev); -#ifdef HAVE_PRIVATE_IOCTL static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd); -#endif -#ifndef NEW_MULTICAST -static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); -#endif -/* Unlike the other PCI cards the 59x cards don't need a large contiguous - memory region, so making the driver a loadable module is feasible. - - Unfortunately maximizing the shared code between the integrated and - module version of the driver results in a complicated set of initialization - procedures. - init_module() -- modules / tc59x_init() -- built-in - The wrappers for vortex_scan() - vortex_scan() The common routine that scans for PCI and EISA cards - vortex_found_device() Allocate a device structure when we find a card. - Different versions exist for modules and built-in. - vortex_probe1() Fill in the device structure -- this is separated - so that the modules code can put it in dev->init. -*/ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ -/* Note: this is the only limit on the number of cards supported!! */ -static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,}; -static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; +/* Option count limit only -- unlimited interfaces are supported. */ +#define MAX_UNITS 8 +static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* A list of all installed Vortex devices, for removing the driver module. */ static struct device *root_vortex_dev = NULL; #ifdef MODULE +#ifndef CARDBUS /* Variables to work-around the Compaq PCI BIOS32 problem. */ static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900; - -static int debug = -1; +#endif #ifdef CARDBUS @@ -520,19 +523,38 @@ static dev_node_t *vortex_attach(dev_locator_t *loc) { - u16 dev_id; + u16 dev_id, vendor_id; u32 io; u8 bus, devfn, irq; struct device *dev; + int chip_idx; if (loc->bus != LOC_PCI) return NULL; bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; - printk(KERN_INFO "vortex_attach(bus %d, function %d)\n", bus, devfn); pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor_id); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); + printk(KERN_INFO "vortex_attach(bus %d, function %d, device %4.4x)\n", + bus, devfn, dev_id); io &= ~3; - dev = vortex_found_device(NULL, io, irq, dev_id, 0, -1); + if (io == 0 || irq == 0) { + printk(KERN_ERR "The 3Com CardBus Ethernet interface was not " + "assigned an %s.\n" KERN_ERR " It will not be activated.\n", + io == 0 ? "I/O address" : "IRQ"); + return NULL; + } + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor_id == pci_tbl[chip_idx].vendor_id + && (dev_id & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) + break; + if (pci_tbl[chip_idx].vendor_id == 0) { /* Compiled out! */ + printk(KERN_INFO "Unable to match chip type %4.4x %4.4x in " + "vortex_attach().\n", vendor_id, dev_id); + return NULL; + } + dev = vortex_probe1(bus, devfn, NULL, io, irq, chip_idx, MAX_UNITS+1); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); @@ -554,61 +576,49 @@ } if (*devp) { struct device *dev = *devp; + struct vortex_private *vp = dev->priv; if (dev->flags & IFF_UP) vortex_close(dev); dev->flags &= ~(IFF_UP|IFF_RUNNING); unregister_netdev(dev); + if (vp->cb_fn_base) iounmap(vp->cb_fn_base); kfree(dev); *devp = *next; + kfree(vp); kfree(node); MOD_DEC_USE_COUNT; } } struct driver_operations vortex_ops = { - "3c59x_cb", vortex_attach, NULL, NULL, vortex_detach + "3c575_cb", vortex_attach, NULL, NULL, vortex_detach }; #endif /* Cardbus support */ -int -init_module(void) +int init_module(void) { - if (debug >= 0) - vortex_debug = debug; if (vortex_debug) - printk(version); - - root_vortex_dev = NULL; + printk(KERN_INFO "%s", version); #ifdef CARDBUS register_driver(&vortex_ops); return 0; #else - { - int cards_found = vortex_scan(0); - if (cards_found == 0) - printk("No 3Com Vortex/Boomerang cards found.\n"); - return cards_found ? 0 : -ENODEV; - } + return vortex_scan(0, pci_tbl); #endif } #else int tc59x_probe(struct device *dev) { - int cards_found = 0; - - cards_found = vortex_scan(dev); - - if (vortex_debug > 0 && cards_found) - printk(version); - - return cards_found ? 0 : -ENODEV; + printk(KERN_INFO "%s", version); + return vortex_scan(dev, pci_tbl); } #endif /* not MODULE */ -static int vortex_scan(struct device *dev) +#ifndef CARDBUS +static int vortex_scan(struct device *dev, struct pci_id_info pci_tbl[]) { int cards_found = 0; @@ -623,25 +633,30 @@ unsigned char pci_bus, pci_device_fn; for (;pci_index < 0xff; pci_index++) { - u8 pci_latency; - u16 pci_command, new_command, vendor, device; - int irq; + u16 vendor, device, pci_command, new_command, pwr_cmd; + int chip_idx, irq; long ioaddr; - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, - pci_index, &pci_bus, &pci_device_fn) + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) break; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) + break; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; + { #if LINUX_VERSION_CODE >= 0x20155 struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[0]; + ioaddr = pdev->base_address[0] & ~3; irq = pdev->irq; #else u32 pci_ioaddr; @@ -650,15 +665,28 @@ PCI_INTERRUPT_LINE, &pci_irq_line); pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr); - ioaddr = pci_ioaddr; + ioaddr = pci_ioaddr & ~3;; irq = pci_irq_line; #endif } - /* Remove I/O space marker in bit 0. */ - ioaddr &= ~3; - if (vendor != TCOM_VENDOR_ID) - continue; + /* Power-up the card. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + 0xe0, &pwr_cmd); + if (pwr_cmd & 0x3) { + /* Save the ioaddr and IRQ info! */ + printk(KERN_INFO " A 3Com network adapter is powered down!" + " Setting the power state %4.4x->%4.4x.\n", + pwr_cmd, pwr_cmd & ~3); + pcibios_write_config_word(pci_bus, pci_device_fn, + 0xe0, pwr_cmd & ~3); + printk(KERN_INFO " Setting the IRQ to %d, IOADDR to %#lx.\n", + irq, ioaddr); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, irq); + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, ioaddr); + } if (ioaddr == 0) { printk(KERN_WARNING " A 3Com network adapter has been found, " @@ -668,34 +696,31 @@ continue; } - if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) continue; /* Activate the card. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); + printk(KERN_INFO " The PCI BIOS has not enabled the device " + "at %d/%d. Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } - dev = vortex_found_device(dev, ioaddr, irq, - device, dev && dev->mem_start - ? dev->mem_start : options[cards_found], - cards_found); + dev = vortex_probe1(pci_bus, pci_device_fn, dev, ioaddr, irq, + chip_idx, cards_found); if (dev) { - struct vortex_private *vp = (struct vortex_private *)dev->priv; /* Get and check the latency values. On the 3c590 series the latency timer must be set to the maximum value to avoid data corruption that occurs when the timer expires during a transfer -- a bug in the Vortex chip only. */ - u8 new_latency = (device&0xff00) == 0x5900 ? 248 : 32; - vp->pci_bus = pci_bus; - vp->pci_dev_fn = pci_device_fn; - vp->pci_device_id = device; + u8 pci_latency; + u8 new_latency = (device & 0xff00) == 0x5900 ? 248 : 32; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); @@ -715,7 +740,7 @@ /* Now check all slots of the EISA bus. */ if (EISA_bus) { - static int ioaddr = 0x1000; + static long ioaddr = 0x1000; for ( ; ioaddr < 0x9000; ioaddr += 0x1000) { int device_id; if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) @@ -727,10 +752,8 @@ device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); if ((device_id & 0xFF00) != 0x5900) continue; - vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12, - device_id, dev && dev->mem_start - ? dev->mem_start : options[cards_found], - cards_found); + vortex_probe1(0, 0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12, + 4, cards_found); dev = 0; cards_found++; } @@ -739,97 +762,58 @@ #ifdef MODULE /* Special code to work-around the Compaq PCI BIOS32 problem. */ if (compaq_ioaddr) { - vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_device_id, - dev && dev->mem_start ? dev->mem_start - : options[cards_found], cards_found); - cards_found++; + vortex_probe1(0, 0, dev, compaq_ioaddr, compaq_irq, + compaq_device_id, cards_found++); dev = 0; } #endif - /* 3c515 cards are now supported by the 3c515.c driver. */ - - return cards_found; + return cards_found ? 0 : -ENODEV; } +#endif /* ! Cardbus */ -static struct device * -vortex_found_device(struct device *dev, int ioaddr, int irq, - int device_id, int option, int card_idx) +static struct device *vortex_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chip_idx, int card_idx) { struct vortex_private *vp; - const char *product_name; - int board_index = 0; + int option; + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ + int i; - for (board_index = 0; product_ids[board_index]; board_index++) { - if (device_id == product_ids[board_index]) - break; - } - /* Handle products we don't recognize, but might still work with. */ - if (product_ids[board_index]) - product_name = product_names[board_index]; - else if ((device_id & 0xff00) == 0x5900) - product_name = "3c590 Vortex"; - else if ((device_id & 0xfff0) == 0x9000) - product_name = "3c900"; - else if ((device_id & 0xfff0) == 0x9050) - product_name = "3c905"; - else { - printk(KERN_WARNING "Unknown 3Com PCI ethernet adapter type %4.4x detected:" - " not configured.\n", device_id); - return 0; - } + dev = init_etherdev(dev, 0); -#ifdef MODULE - /* Allocate and fill new device structure. */ - { - int dev_size = sizeof(struct device) + - sizeof(struct vortex_private) + 15; /* Pad for alignment */ + printk(KERN_INFO "%s: 3Com %s at 0x%lx, ", + dev->name, pci_tbl[chip_idx].name, ioaddr); - dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); - memset(dev, 0, dev_size); - } - /* Align the Rx and Tx ring entries. */ - dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15); - vp = (struct vortex_private *)dev->priv; - dev->name = vp->devname; /* An empty string. */ dev->base_addr = ioaddr; dev->irq = irq; - dev->init = vortex_probe1; - vp->product_name = product_name; - vp->options = option; - if (card_idx >= 0) { - if (full_duplex[card_idx] >= 0) - vp->full_duplex = full_duplex[card_idx]; - } else - vp->full_duplex = (option > 0 && (option & 0x10) ? 1 : 0); + dev->mtu = mtu; - if (option > 0) { - vp->media_override = ((option & 7) == XCVR_10baseTOnly) ? - XCVR_10baseT : option & 7; - vp->bus_master = (option & 16) ? 1 : 0; - } else { - vp->media_override = 7; - vp->bus_master = 0; + /* Make certain the descriptor lists are aligned. */ + { + void *mem = kmalloc(sizeof(*vp) + 15, GFP_KERNEL); + vp = (void *)(((long)mem + 15) & ~15); + vp->priv_addr = mem; } - ether_setup(dev); + memset(vp, 0, sizeof(*vp)); + dev->priv = vp; + vp->next_module = root_vortex_dev; root_vortex_dev = dev; - if (register_netdev(dev) != 0) - return 0; -#else /* not a MODULE */ - if (dev) { - /* Caution: quad-word alignment required for rings! */ - dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); - memset(dev->priv, 0, sizeof (struct vortex_private)); - } - dev = init_etherdev(dev, sizeof(struct vortex_private)); - dev->base_addr = ioaddr; - dev->irq = irq; - dev->mtu = mtu; - vp = (struct vortex_private *)dev->priv; - vp->product_name = product_name; - vp->options = option; + vp->chip_id = chip_idx; + vp->pci_bus = pci_bus; + vp->pci_devfn = pci_devfn; + + /* The lower four bits are the media type. */ + if (dev->mem_start) + option = dev->mem_start; + else if (card_idx < MAX_UNITS) + option = options[card_idx]; + else + option = -1; + if (option >= 0) { vp->media_override = ((option & 7) == 2) ? 0 : option & 7; vp->full_duplex = (option & 8) ? 1 : 0; @@ -839,22 +823,11 @@ vp->full_duplex = 0; vp->bus_master = 0; } + if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) + vp->full_duplex = 1; - vortex_probe1(dev); -#endif /* MODULE */ - return dev; -} - -static int vortex_probe1(struct device *dev) -{ - int ioaddr = dev->base_addr; - struct vortex_private *vp = (struct vortex_private *)dev->priv; - u16 *ether_addr = (u16 *)dev->dev_addr; - unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ - int i; - - printk(KERN_INFO "%s: 3Com %s at %#3x,", - dev->name, vp->product_name, ioaddr); + vp->force_fd = vp->full_duplex; + vp->options = option; /* Read the station address from the EEPROM. */ EL3WINDOW(0); @@ -885,14 +858,30 @@ printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); for (i = 0; i < 3; i++) - ether_addr[i] = htons(eeprom[i + 10]); + ((u16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]); for (i = 0; i < 6; i++) printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); +#ifdef __sparc__ + printk(", IRQ %s\n", __irq_itoa(dev->irq)); +#else printk(", IRQ %d\n", dev->irq); /* Tell them about an invalid IRQ. */ if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS)) printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n", dev->irq); +#endif + + if (pci_tbl[vp->chip_id].drv_flags & HAS_CB_FNS) { + u32 fn_st_addr; /* Cardbus function status space */ + pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_2, + &fn_st_addr); + if (fn_st_addr) + vp->cb_fn_base = ioremap(fn_st_addr & ~3, 128); + printk("%s: CardBus functions mapped %8.8x->%p (PCMCIA committee" + " brain-damage).\n", dev->name, fn_st_addr, vp->cb_fn_base); + EL3WINDOW(2); + outw(0x10 | inw(ioaddr + Wn2_ResetOptions), ioaddr + Wn2_ResetOptions); + } /* Extract our information from the EEPROM data. */ vp->info1 = eeprom[13]; @@ -918,7 +907,7 @@ config.u.ram_width ? "word" : "byte", ram_split[config.u.ram_split], config.u.autoselect ? "autoselect/" : "", - config.u.xcvr ? "NWay Autonegotiation" : + config.u.xcvr > XCVR_ExtMII ? "" : media_tbl[config.u.xcvr].name); vp->default_media = config.u.xcvr; vp->autoselect = config.u.autoselect; @@ -931,22 +920,24 @@ } else dev->if_port = vp->default_media; - if (dev->if_port == XCVR_MII) { + if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { int phy, phy_idx = 0; EL3WINDOW(4); - for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) { - int mii_status; - mdio_sync(ioaddr, 32); - mii_status = mdio_read(ioaddr, phy, 1); + mii_preamble_required++; + mii_preamble_required++; + mdio_read(ioaddr, 24, 1); + for (phy = 1; phy <= 32 && phy_idx < sizeof(vp->phys); phy++) { + int mii_status, phyx = phy & 0x1f; + mii_status = mdio_read(ioaddr, phyx, 1); if (mii_status && mii_status != 0xffff) { - vp->phys[phy_idx++] = phy; - printk(KERN_INFO " MII transceiver found at address %d, status %4x.\n", - phy, mii_status); - mdio_sync(ioaddr, 32); - if ((mdio_read(ioaddr, phy, 1) & 0x0040) == 0) - mii_preamble_required = 1; + vp->phys[phy_idx++] = phyx; + printk(KERN_INFO " MII transceiver found at address %d," + " status %4x.\n", phyx, mii_status); + if ((mii_status & 0x0040) == 0) + mii_preamble_required++; } } + mii_preamble_required--; if (phy_idx == 0) { printk(KERN_WARNING" ***WARNING*** No MII transceivers found!\n"); vp->phys[0] = 24; @@ -954,7 +945,7 @@ vp->advertising = mdio_read(ioaddr, vp->phys[0], 4); if (vp->full_duplex) { /* Only advertise the FD media types. */ - vp->advertising &= 0x015F; + vp->advertising &= ~0x02A0; mdio_write(ioaddr, vp->phys[0], 4, vp->advertising); } } @@ -968,30 +959,24 @@ } /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name); + request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); /* The 3c59x-specific entries in the device structure. */ dev->open = &vortex_open; dev->hard_start_xmit = &vortex_start_xmit; dev->stop = &vortex_close; dev->get_stats = &vortex_get_stats; -#ifdef HAVE_PRIVATE_IOCTL dev->do_ioctl = &vortex_ioctl; -#endif -#ifdef NEW_MULTICAST dev->set_multicast_list = &set_rx_mode; -#else - dev->set_multicast_list = &set_multicast_list; -#endif - return 0; + return dev; } static int vortex_open(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct vortex_private *vp = (struct vortex_private *)dev->priv; union wn3_config config; int i; @@ -1006,28 +991,31 @@ dev->name, vp->media_override, media_tbl[vp->media_override].name); dev->if_port = vp->media_override; + } else if (vp->autoselect && pci_tbl[vp->chip_id].drv_flags & HAS_NWAY) { + dev->if_port = XCVR_NWAY; } else if (vp->autoselect) { /* Find first available media type, starting with 100baseTx. */ dev->if_port = XCVR_100baseTx; while (! (vp->available_media & media_tbl[dev->if_port].mask)) dev->if_port = media_tbl[dev->if_port].next; - - if (vortex_debug > 1) - printk(KERN_DEBUG "%s: Initial media type %s.\n", - dev->name, media_tbl[dev->if_port].name); - - init_timer(&vp->timer); - vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); - vp->timer.data = (unsigned long)dev; - vp->timer.function = &vortex_timer; /* timer handler */ - add_timer(&vp->timer); } else dev->if_port = vp->default_media; + init_timer(&vp->timer); + vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); + vp->timer.data = (unsigned long)dev; + vp->timer.function = &vortex_timer; /* timer handler */ + add_timer(&vp->timer); + + if (vortex_debug > 1) + printk(KERN_DEBUG "%s: Initial media type %s.\n", + dev->name, media_tbl[dev->if_port].name); + + vp->full_duplex = vp->force_fd; config.u.xcvr = dev->if_port; outl(config.i, ioaddr + Wn3_Config); - if (dev->if_port == XCVR_MII) { + if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { int mii_reg1, mii_reg5; EL3WINDOW(4); /* Read BMSR (reg1) only to clear old status. */ @@ -1067,20 +1055,10 @@ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); -#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } -#else - if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL) - return -EAGAIN; - irq2dev_map[dev->irq] = dev; - if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) { - irq2dev_map[dev->irq] = NULL; - return -EAGAIN; - } -#endif if (vortex_debug > 1) { EL3WINDOW(4); @@ -1098,9 +1076,11 @@ if (dev->if_port == XCVR_10base2) /* Start the thinnet transceiver. We should really wait 50ms...*/ outw(StartCoax, ioaddr + EL3_CMD); - EL3WINDOW(4); - outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) | - media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + if (dev->if_port != XCVR_NWAY) { + EL3WINDOW(4); + outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) | + media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + } /* Switch to the stats window, and clear all stats by reading. */ outw(StatsDisable, ioaddr + EL3_CMD); @@ -1127,22 +1107,23 @@ printk(KERN_DEBUG "%s: Filling in the Rx ring.\n", dev->name); for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; - vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]); + vp->rx_ring[i].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[i+1])); vp->rx_ring[i].status = 0; /* Clear complete bit. */ - vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG; - skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + vp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LAST_FRAG); + skb = dev_alloc_skb(PKT_BUF_SZ); vp->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */ #if LINUX_VERSION_CODE >= 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[i].addr = virt_to_bus(skb->tail); + vp->rx_ring[i].addr = cpu_to_le32(virt_to_bus(skb->tail)); #else vp->rx_ring[i].addr = virt_to_bus(skb->data); #endif } - vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ + /* Wrap the ring. */ + vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0])); outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); } if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ @@ -1170,14 +1151,16 @@ (vp->full_bus_master_tx ? DownComplete : TxAvailable) | (vp->full_bus_master_rx ? UpComplete : RxComplete) | (vp->bus_master ? DMADone : 0); + vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable | RxComplete | + StatsFull | HostError | TxComplete | IntReq + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete; outw(vp->status_enable, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | HostError | TxComplete - | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, - ioaddr + EL3_CMD); + outw(vp->intr_enable, ioaddr + EL3_CMD); + if (vp->cb_fn_base) /* The PCMCIA people are idiots. */ + writel(0x8000, vp->cb_fn_base + 4); MOD_INC_USE_COUNT; @@ -1186,24 +1169,23 @@ static void vortex_timer(unsigned long data) { -#ifdef AUTOMEDIA struct device *dev = (struct device *)data; struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; - unsigned long flags; + long ioaddr = dev->base_addr; + int next_tick = 0; int ok = 0; + int media_status, mii_status, old_window; if (vortex_debug > 1) printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n", dev->name, media_tbl[dev->if_port].name); - save_flags(flags); cli(); { - int old_window = inw(ioaddr + EL3_CMD) >> 13; - int media_status; - EL3WINDOW(4); - media_status = inw(ioaddr + Wn4_Media); - switch (dev->if_port) { - case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx: + disable_irq(dev->irq); + old_window = inw(ioaddr + EL3_CMD) >> 13; + EL3WINDOW(4); + media_status = inw(ioaddr + Wn4_Media); + switch (dev->if_port) { + case XCVR_10baseT: case XCVR_100baseTx: case XCVR_100baseFx: if (media_status & Media_LnkBeat) { ok = 1; if (vortex_debug > 1) @@ -1212,27 +1194,40 @@ } else if (vortex_debug > 1) printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); - break; - case XCVR_MII: - { - int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); + case XCVR_MII: case XCVR_NWAY: + mii_status = mdio_read(ioaddr, vp->phys[0], 1); + ok = 1; + if (debug > 1) + printk(KERN_DEBUG "%s: MII transceiver has status %4.4x.\n", + dev->name, mii_status); + if (mii_status & 0x0004) { int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); - if (vortex_debug > 1) - printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, " - "link partner capability %4.4x.\n", - dev->name, vp->phys[0], mii_reg1, mii_reg5); - if (mii_reg1 & 0x0004) - ok = 1; - break; + if (! vp->force_fd && mii_reg5 != 0xffff) { + int duplex = (mii_reg5&0x0100) || + (mii_reg5 & 0x01C0) == 0x0040; + if (vp->full_duplex != duplex) { + vp->full_duplex = duplex; + printk(KERN_INFO "%s: Setting %s-duplex based on MII " + "#%d link partner capability of %4.4x.\n", + dev->name, vp->full_duplex ? "full" : "half", + vp->phys[0], mii_reg5); + /* Set the full-duplex bit. */ + outb((vp->full_duplex ? 0x20 : 0) | + (dev->mtu > 1500 ? 0x40 : 0), + ioaddr + Wn3_MAC_Ctrl); + } + next_tick = 60*HZ; + } } + break; default: /* Other media types handled by Tx timeouts. */ if (vortex_debug > 1) printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); ok = 1; - } - if ( ! ok) { + } + if ( ! ok) { union wn3_config config; do { @@ -1249,8 +1244,7 @@ printk(KERN_DEBUG "%s: Media selection failed, now trying " "%s port.\n", dev->name, media_tbl[dev->if_port].name); - vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); - add_timer(&vp->timer); + next_tick = RUN_AT(media_tbl[dev->if_port].wait); } outw((media_status & ~(Media_10TP|Media_SQE)) | media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); @@ -1262,21 +1256,25 @@ outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax, ioaddr + EL3_CMD); - } - EL3WINDOW(old_window); - } restore_flags(flags); - if (vortex_debug > 1) + } + EL3WINDOW(old_window); + enable_irq(dev->irq); + + if (vortex_debug > 2) printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n", dev->name, media_tbl[dev->if_port].name); -#endif /* AUTOMEDIA*/ + if (next_tick) { + vp->timer.expires = RUN_AT(next_tick); + add_timer(&vp->timer); + } return; } static void vortex_tx_timeout(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int j; printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n", @@ -1290,7 +1288,7 @@ printk(KERN_ERR "%s: Interrupt posted but not delivered --" " IRQ blocked by another device?\n", dev->name); /* Bad idea here.. but we might as well handle a few events. */ - vortex_interrupt IRQ(dev->irq, dev, 0); + vortex_interrupt(dev->irq, dev, 0); } outw(TxReset, ioaddr + EL3_CMD); for (j = 200; j >= 0 ; j--) @@ -1309,8 +1307,8 @@ for (i = 0; i < TX_RING_SIZE; i++) { printk(KERN_DEBUG " %d: @%p length %8.8x status %8.8x\n", i, &vp->tx_ring[i], - vp->tx_ring[i].length, - vp->tx_ring[i].status); + le32_to_cpu(vp->tx_ring[i].length), + le32_to_cpu(vp->tx_ring[i].status)); } } #endif @@ -1347,7 +1345,7 @@ vortex_error(struct device *dev, int status) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int do_tx_reset = 0; int i; @@ -1387,8 +1385,10 @@ DoneDidThat++; } } - if (status & IntReq) /* Restore all interrupt sources. */ - outw(ioaddr + EL3_CMD, vp->status_enable); + if (status & IntReq) { /* Restore all interrupt sources. */ + outw(vp->status_enable, ioaddr + EL3_CMD); + outw(vp->intr_enable, ioaddr + EL3_CMD); + } if (status & HostError) { u16 fifo_diag; EL3WINDOW(4); @@ -1434,7 +1434,7 @@ vortex_start_xmit(struct sk_buff *skb, struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start >= TX_TIMEOUT) @@ -1444,7 +1444,6 @@ /* Put out the doubleword header... */ outl(skb->len, ioaddr + TX_FIFO); -#ifdef VORTEX_BUS_MASTER if (vp->bus_master) { /* Set the bus-master controller to transfer the packet. */ outl(virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); @@ -1462,16 +1461,6 @@ /* Interrupt us when the FIFO has room for max-sized packet. */ outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); } -#else - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - DEV_FREE_SKB(skb); - if (inw(ioaddr + TxFree) > 1536) { - clear_bit(0, (void*)&dev->tbusy); - } else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); -#endif /* bus master */ dev->trans_start = jiffies; @@ -1506,7 +1495,7 @@ boomerang_start_xmit(struct sk_buff *skb, struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start >= TX_TIMEOUT) @@ -1528,13 +1517,12 @@ printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n", dev->name); return 1; - } - /* end change 06/25/97 M. Sievers */ + } vp->tx_skbuff[entry] = skb; vp->tx_ring[entry].next = 0; - vp->tx_ring[entry].addr = virt_to_bus(skb->data); - vp->tx_ring[entry].length = skb->len | LAST_FRAG; - vp->tx_ring[entry].status = skb->len | TxIntrUploaded; + vp->tx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->data)); + vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG); + vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded); save_flags(flags); cli(); @@ -1543,7 +1531,7 @@ for (i = 600; i >= 0 ; i--) if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) break; - prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); + prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry])); if (inl(ioaddr + DownListPtr) == 0) { outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); queued_packet++; @@ -1555,7 +1543,7 @@ if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) vp->tx_full = 1; else { /* Clear previous interrupt enable. */ - prev_entry->status &= ~TxIntrUploaded; + prev_entry->status &= cpu_to_le32(~TxIntrUploaded); clear_bit(0, (void*)&dev->tbusy); } dev->trans_start = jiffies; @@ -1565,28 +1553,33 @@ /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) +static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs) { -#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = dev_id; -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif - struct vortex_private *vp; - int ioaddr, status; - int latency; + struct vortex_private *vp = (struct vortex_private *)dev->priv; + long ioaddr; + int latency, status; int work_done = max_interrupt_work; - vp = (struct vortex_private *)dev->priv; - if (test_and_set_bit(0, (void*)&vp->in_interrupt)) { +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ + return; + } +#else + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } + dev->interrupt = 1; +#endif dev->interrupt = 1; ioaddr = dev->base_addr; latency = inb(ioaddr + Timer); - status = inw(ioaddr + EL3_STATUS); if (vortex_debug > 4) @@ -1635,17 +1628,23 @@ mark_bh(NET_BH); } } -#ifdef VORTEX_BUS_MASTER if (status & DMADone) { - outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ - clear_bit(0, (void*)&dev->tbusy); - DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */ - mark_bh(NET_BH); + if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) { + outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ + DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */ + if (inw(ioaddr + TxFree) > 1536) { + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } else /* Interrupt when FIFO has room for max-sized packet. */ + outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); + } } -#endif /* Check for all uncommon interrupts at once. */ - if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) + if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) { + if (status == 0xffff) + break; vortex_error(dev, status); + } if (--work_done < 0) { if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) { @@ -1658,13 +1657,14 @@ /* Disable all pending interrupts. */ outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); - /* Set a timer to reenable interrupts. */ - + /* The timer will reenable interrupts. */ break; } } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + if (vp->cb_fn_base) /* The PCMCIA people are idiots. */ + writel(0x8000, vp->cb_fn_base + 4); } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); @@ -1672,16 +1672,18 @@ printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n", dev->name, status); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; - clear_bit(0, (void*)&vp->in_interrupt); +#endif return; } -static int -vortex_rx(struct device *dev) +static int vortex_rx(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i; short rx_status; @@ -1704,25 +1706,28 @@ int pkt_len = rx_status & 0x1fff; struct sk_buff *skb; - skb = DEV_ALLOC_SKB(pkt_len + 5); + skb = dev_alloc_skb(pkt_len + 5); if (vortex_debug > 4) printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; -#if LINUX_VERSION_CODE >= 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), - (pkt_len + 3) >> 2); + if (vp->bus_master && + ! (inw(ioaddr + Wn7_MasterStatus) & 0x8000)) { + outl(virt_to_bus(skb_put(skb, pkt_len)), + ioaddr + Wn7_MasterAddr); + outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); + outw(StartDMAUp, ioaddr + EL3_CMD); + while (inw(ioaddr + Wn7_MasterStatus) & 0x8000) + ; + } else { + insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), + (pkt_len + 3) >> 2); + } outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb, dev); -#else - skb->len = pkt_len; - /* 'skb->data' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ -#endif /* KERNEL_1_3_0 */ netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; @@ -1751,7 +1756,7 @@ { struct vortex_private *vp = (struct vortex_private *)dev->priv; int entry = vp->cur_rx % RX_RING_SIZE; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int rx_status; int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx; @@ -1759,8 +1764,9 @@ printk(KERN_DEBUG " In boomerang_rx(), status %4.4x, rx_status " "%4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); - while ((--rx_work_limit >= 0) && - ((rx_status = vp->rx_ring[entry].status) & RxDComplete)) { + while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete){ + if (--rx_work_limit < 0) + break; if (rx_status & RxDError) { /* Error, update stats. */ unsigned char rx_error = rx_status >> 16; if (vortex_debug > 2) @@ -1783,41 +1789,28 @@ /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak - && (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) { + && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { skb->dev = dev; -#if LINUX_VERSION_CODE >= 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), - bus_to_virt(vp->rx_ring[entry].addr), + bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)), pkt_len); -#else - memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len); - skb->len = pkt_len; -#endif rx_copy++; - } else{ + } else { void *temp; /* Pass up the skbuff already on the Rx ring. */ skb = vp->rx_skbuff[entry]; - if (skb == NULL) { - printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name); - break; - } vp->rx_skbuff[entry] = NULL; -#if LINUX_VERSION_CODE >= 0x10300 temp = skb_put(skb, pkt_len); -#else - temp = skb->data; -#endif /* Remove this checking code for final release. */ - if (bus_to_virt(vp->rx_ring[entry].addr) != temp) + if (bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)) != temp) printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match" " in boomerang_rx: %p vs. %p.\n", dev->name, - bus_to_virt(vp->rx_ring[entry].addr), temp); + bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)), + temp); rx_nocopy++; } -#if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); { /* Use hardware checksum info. */ int csum_bits = rx_status & 0xee000000; @@ -1828,9 +1821,6 @@ rx_csumhits++; } } -#else - skb->len = pkt_len; -#endif netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; @@ -1842,29 +1832,17 @@ struct sk_buff *skb; entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { - skb = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (skb == NULL) { - printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name); + skb = dev_alloc_skb(PKT_BUF_SZ); + if (skb == NULL) break; /* Bad news! */ - } skb->dev = dev; /* Mark as being used by this device. */ -#if LINUX_VERSION_CODE > 0x10300 skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[entry].addr = virt_to_bus(skb->tail); -#else - vp->rx_ring[entry].addr = virt_to_bus(skb->data); -#endif + vp->rx_ring[entry].addr = cpu_to_le32(virt_to_bus(skb->tail)); vp->rx_skbuff[entry] = skb; } vp->rx_ring[entry].status = 0; /* Clear complete bit. */ outw(UpUnstall, ioaddr + EL3_CMD); } - - if (vp->dirty_rx >= RX_RING_SIZE ) { - vp->cur_rx -= RX_RING_SIZE; - vp->dirty_rx -= RX_RING_SIZE; - } - return 0; } @@ -1872,7 +1850,7 @@ vortex_close(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i; dev->start = 0; @@ -1899,12 +1877,7 @@ /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); @@ -1934,8 +1907,7 @@ return 0; } -static struct enet_statistics * -vortex_get_stats(struct device *dev) +static struct net_device_stats *vortex_get_stats(struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; unsigned long flags; @@ -1956,7 +1928,7 @@ table. This is done by checking that the ASM (!) code generated uses atomic updates with '+='. */ -static void update_stats(int ioaddr, struct device *dev) +static void update_stats(long ioaddr, struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; @@ -1987,20 +1959,14 @@ return; } -#ifdef HAVE_PRIVATE_IOCTL static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd) { struct vortex_private *vp = (struct vortex_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; u16 *data = (u16 *)&rq->ifr_data; int phy = vp->phys[0] & 0x1f; - if (vortex_debug > 2) - printk(KERN_DEBUG "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", - dev->name, rq->ifr_ifrn.ifrn_name, cmd, - data[0], data[1], data[2], data[3]); - - switch(cmd) { + switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = phy; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ @@ -2010,22 +1976,20 @@ case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!suser()) return -EPERM; + EL3WINDOW(4); mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; } } -#endif /* HAVE_PRIVATE_IOCTL */ -/* This new version of set_rx_mode() supports v1.4 kernels. - The Vortex chip has no documented multicast filter, so the only +/* Pre-Cyclone chips have no documented multicast filter, so the only multicast setting is to receive all multicast frames. At least the chip has a very clean way to set the mode, unlike many others. */ -static void -set_rx_mode(struct device *dev) +static void set_rx_mode(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int new_mode; if (dev->flags & IFF_PROMISC) { @@ -2039,14 +2003,6 @@ outw(new_mode, ioaddr + EL3_CMD); } -#ifndef NEW_MULTICAST -/* The old interface to set the Rx mode. */ -static void -set_multicast_list(struct device *dev, int num_addrs, void *addrs) -{ - set_rx_mode(dev); -} -#endif /* MII transceiver control section. @@ -2057,7 +2013,7 @@ /* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually met by back-to-back PCI I/O cycles, but we insert a delay to avoid "overclocking" issues. */ -#define mdio_delay() udelay(1) +#define mdio_delay() inl(mdio_addr) #define MDIO_SHIFT_CLK 0x01 #define MDIO_DIR_WRITE 0x04 @@ -2068,11 +2024,11 @@ /* Generate the preamble required for initial synchronization and a few older transceivers. */ -static void mdio_sync(int ioaddr, int bits) +static void mdio_sync(long ioaddr, int bits) { - int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + long mdio_addr = ioaddr + Wn4_PhysicalMgmt; - /* Establish sync by sending at least 32 logic ones. */ + /* Establish sync by sending at least 32 logic ones. */ while (-- bits >= 0) { outw(MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -2081,12 +2037,12 @@ } } -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(long ioaddr, int phy_id, int location) { int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; unsigned int retval = 0; - int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + long mdio_addr = ioaddr + Wn4_PhysicalMgmt; if (mii_preamble_required) mdio_sync(ioaddr, 32); @@ -2107,13 +2063,17 @@ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); } - return retval>>1 & 0xffff; +#if 0 + return (retval>>1) & 0x1ffff; +#else + return retval & 0x20000 ? 0xffff : retval>>1 & 0xffff; +#endif } -static void mdio_write(int ioaddr, int phy_id, int location, int value) +static void mdio_write(long ioaddr, int phy_id, int location, int value) { int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; - int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + long mdio_addr = ioaddr + Wn4_PhysicalMgmt; int i; if (mii_preamble_required) @@ -2140,8 +2100,7 @@ #ifdef MODULE -void -cleanup_module(void) +void cleanup_module(void) { struct device *next_dev; @@ -2151,11 +2110,14 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_vortex_dev) { - next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module; + struct vortex_private *vp=(void *)(root_vortex_dev->priv); + next_dev = vp->next_module; unregister_netdev(root_vortex_dev); outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD); - release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE); + release_region(root_vortex_dev->base_addr, + pci_tbl[vp->chip_id].io_size); kfree(root_vortex_dev); + kfree(vp->priv_addr); root_vortex_dev = next_dev; } } @@ -2166,7 +2128,7 @@ * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c" - * compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/depca.c linux/drivers/net/depca.c --- linux.vanilla/drivers/net/depca.c Sun Jun 21 18:41:04 1998 +++ linux/drivers/net/depca.c Sun Dec 6 03:16:27 1998 @@ -154,7 +154,20 @@ the 'memory autoprobe' picking the wrong shared memory (for the case of 2 depca's in a PC). + ************************************************************************ + Support for MCA EtherWORKS cards added 11-3-98. + Verified to work with up to 2 DE212 cards in a system (although not + fully stress-tested). + + Currently known bugs/limitations: + + Note: with the MCA stuff as a module, it trusts the MCA configuration, + not the command line for IRQ and memory address. You can + specify them if you want, but it will throw your values out. + You still have to pass the IO address it was configured as + though. + ************************************************************************ TO DO: ------ @@ -202,11 +215,16 @@ 0.422 29-Apr-96 Fix depca_hw_init() bug 0.423 7-Jun-96 Fix module load bug 0.43 16-Aug-96 Update alloc_device() to conform to de4x5.c + 0.44 1-Sep-97 Fix *_probe() to test check_region() first - bug + reported by + 0.45 3-Nov-98 Added support for MCA EtherWORKS (DE210/DE212) cards + by + 0.451 5-Nov-98 Fixed mca stuff cuz I'm a dummy. ========================================================================= */ -static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n"; +static const char *version = "depca.c:v0.451 1998/11/14 davies@maniac.ultranet.com\n"; #include @@ -233,6 +251,10 @@ #include #include +#ifdef CONFIG_MCA +#include +#endif + #include "depca.h" #ifdef DEPCA_DEBUG @@ -280,15 +302,20 @@ static short mem_chkd = 0; /* +** Adapter ID for the MCA EtherWORKS DE210/212 adapter +*/ +#define DE212_ID 0x6def + +/* ** Name <-> Adapter mapping */ #define DEPCA_SIGNATURE {"DEPCA",\ "DE100","DE101",\ "DE200","DE201","DE202",\ - "DE210",\ + "DE210","DE212",\ "DE422",\ ""} -static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter; +static enum {DEPCA, de100, de101, de200, de201, de202, de210, de212, de422, unknown} adapter; /* ** Miscellaneous info... @@ -342,7 +369,7 @@ char devname[DEPCA_STRLEN]; /* Device Product String */ char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */ char adapter; /* Adapter type */ - struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ + char mca_slot; /* MCA slot, if MCA else -1 */ struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ struct depca_init init_block;/* Shadow Initialization block */ char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */ @@ -393,7 +420,7 @@ /* ** Private functions */ -static int depca_hw_init(struct device *dev, u_long ioaddr); +static int depca_hw_init(struct device *dev, u_long ioaddr, int mca_slot); static void depca_init_ring(struct device *dev); static int depca_rx(struct device *dev); static int depca_tx(struct device *dev); @@ -407,6 +434,9 @@ static void SetMulticastFilter(struct device *dev); static void isa_probe(struct device *dev, u_long iobase); static void eisa_probe(struct device *dev, u_long iobase); +#ifdef CONFIG_MCA +static void mca_probe(struct device *dev, u_long iobase); +#endif static struct device *alloc_device(struct device *dev, u_long iobase); static int depca_dev_index(char *s); static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); @@ -441,7 +471,8 @@ -int depca_probe(struct device *dev) +int +depca_probe(struct device *dev) { int tmp = num_depcas, status = -ENODEV; u_long iobase = dev->base_addr; @@ -450,6 +481,9 @@ printk("Autoprobing is not supported when loading a module based driver.\n"); status = -EIO; } else { +#ifdef CONFIG_MCA + mca_probe(dev, iobase); +#endif isa_probe(dev, iobase); eisa_probe(dev, iobase); @@ -472,7 +506,7 @@ } static int -depca_hw_init(struct device *dev, u_long ioaddr) +depca_hw_init(struct device *dev, u_long ioaddr, int mca_slot) { struct depca_private *lp; int i, j, offset, netRAM, mem_len, status=0; @@ -495,7 +529,10 @@ if ((adapter != unknown) && mem_start) { /* found a DEPCA device */ dev->base_addr = ioaddr; - if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */ + if (mca_slot != -1) { + printk("%s: %s at 0x%04lx (MCA slot %d)", dev->name, name, + ioaddr, mca_slot); + } else if ((ioaddr & 0x0fff) == DEPCA_EISA_IO_PORTS) { /* EISA slot address */ printk("%s: %s at 0x%04lx (EISA slot %d)", dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f)); } else { /* ISA port address */ @@ -541,7 +578,8 @@ lp = (struct depca_private *)dev->priv; memset((char *)dev->priv, 0, sizeof(struct depca_private)); lp->adapter = adapter; - sprintf(lp->adapter_name,"%s (%s)", name, dev->name); + lp->mca_slot = mca_slot; + sprintf(lp->adapter_name,"%s (%s)", name, dev->name); request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name); /* Initialisation Block */ @@ -603,6 +641,7 @@ case de201: case de202: case de210: + case de212: depca_irq = de2xx_irq; break; case de422: @@ -1205,10 +1244,161 @@ return; } +#ifdef CONFIG_MCA +/* +** Microchannel bus I/O device probe +*/ +static void +mca_probe(struct device *dev, u_long ioaddr) +{ + unsigned char pos[2]; + unsigned char where; + unsigned long iobase; + int irq; + int slot = 0; + + /* + ** See if we've been here before. + */ + if ((!ioaddr && autoprobed) || (ioaddr && !loading_module)) return; + + if (MCA_bus) { + /* + ** Search for the adapter. If an address has been given, search + ** specifically for the card at that address. Otherwise find the + ** first card in the system. + */ + while ((dev!=NULL) && + ((slot=mca_find_adapter(DE212_ID, slot)) != MCA_NOTFOUND)) { + pos[0] = mca_read_stored_pos(slot, 2); + pos[1] = mca_read_stored_pos(slot, 3); + + /* + ** IO of card is handled by bits 1 and 2 of pos0. + ** + ** bit2 bit1 IO + ** 0 0 0x2c00 + ** 0 1 0x2c10 + ** 1 0 0x2c20 + ** 1 1 0x2c30 + */ + where = (pos[0] & 6) >> 1; + iobase = 0x2c00 + (0x10 * where); + + if ((ioaddr) && (ioaddr != iobase)) { + /* + ** Card was found, but not at the right IO location. Continue + ** scanning from the next MCA slot up for another card. + */ + slot++; + continue; + } + + /* + ** Found the adapter we were looking for. Now start setting it up. + ** + ** First work on decoding the IRQ. It's stored in the lower 4 bits + ** of pos1. Bits are as follows (from the ADF file): + ** + ** Bits + ** 3 2 1 0 IRQ + ** -------------------- + ** 0 0 1 0 5 + ** 0 0 0 1 9 + ** 0 1 0 0 10 + ** 1 0 0 0 11 + **/ + where = pos[1] & 0x0f; + switch(where) { + case 1: + irq = 9; + break; + case 2: + irq = 5; + break; + case 4: + irq = 10; + break; + case 8: + irq = 11; + break; + default: + printk("%s: mca_probe IRQ error. You should never get here (%d).\n", dev->name, where); + return; + } + + /* + ** Shared memory address of adapter is stored in bits 3-5 of pos0. + ** They are mapped as follows: + ** + ** Bit + ** 5 4 3 Memory Addresses + ** 0 0 0 C0000-CFFFF (64K) + ** 1 0 0 C8000-CFFFF (32K) + ** 0 0 1 D0000-DFFFF (64K) + ** 1 0 1 D8000-DFFFF (32K) + ** 0 1 0 E0000-EFFFF (64K) + ** 1 1 0 E8000-EFFFF (32K) + */ + where = (pos[0] & 0x18) >> 3; + mem = 0xc0000 + (where * 0x10000); + if (pos[0] & 0x20) { + mem += 0x8000; + } + + /* + ** Get everything allocated and initialized... (almost just + ** like the ISA and EISA probes) + */ + if (DevicePresent(iobase) != 0) { + /* + ** If the MCA configuration says the card should be here, + ** it really should be here. + */ + printk(KERN_ERR "%s: MCA reports card at 0x%lx but it is not +responding.\n", dev->name, iobase); + } + + if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) { + if ((dev = alloc_device(dev, iobase)) != NULL) { + dev->irq = irq; + if (depca_hw_init(dev, iobase, slot) == 0) { + /* + ** Adapter initialized correctly: Name it in + ** /proc/mca. + */ + mca_set_adapter_name(slot, "DE210/212 Ethernet Adapter"); + mca_mark_as_used(slot); + num_depcas++; + } + num_eth++; + } + } else if (autoprobed) { + printk(KERN_WARNING "%s: region already allocated at 0x%04lx.\n", dev->name, iobase); + } + + /* + ** If this is a probe by a module, return after setting up the + ** given card. + */ + if (ioaddr) return; + + /* + ** Set up to check the next slot and loop. + */ + slot++; + } + } + + return; +} +#endif + /* ** ISA bus I/O device probe */ -static void isa_probe(struct device *dev, u_long ioaddr) +static void +isa_probe(struct device *dev, u_long ioaddr) { int i = num_depcas, maxSlots; s32 ports[] = DEPCA_IO_PORTS; @@ -1225,17 +1415,17 @@ } for (; (iname,ports[i]); } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]); } } @@ -1246,7 +1436,8 @@ ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually ** the motherboard. Upto 15 EISA devices are supported. */ -static void eisa_probe(struct device *dev, u_long ioaddr) +static void +eisa_probe(struct device *dev, u_long ioaddr) { int i, maxSlots; u_long iobase; @@ -1267,19 +1458,19 @@ if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS; for (; (iname,iobase); } } + } else if (autoprobed) { + printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase); } } @@ -1683,7 +1874,7 @@ /* ** Perform IOCTL call functions here. Some are privileged operations and the ** effective uid is checked in those cases. -** All MCA IOCTLs will not work here and are for testing purposes only. +** All multicast IOCTLs will not work here and are for testing purposes only. */ static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd) { @@ -1703,126 +1894,103 @@ tmp.addr[i] = dev->dev_addr[i]; } ioc->len = ETH_ALEN; - if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); - } - + if (verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len)) return -EFAULT; + memcpy_tofs(ioc->data, tmp.addr, ioc->len); break; + case DEPCA_SET_HWADDR: /* Set the hardware address */ - if (suser()) { - if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { - memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN); - for (i=0; idev_addr[i] = tmp.addr[i]; - } - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } - } else { - status = -EPERM; + if (!suser()) return -EPERM; + if (verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN)) return -EFAULT; + memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN); + for (i=0; idev_addr[i] = tmp.addr[i]; } + while(dev->tbusy) barrier(); /* Stop ring access */ + set_bit(0, (void*)&dev->tbusy); + while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ break; + case DEPCA_SET_PROM: /* Set Promiscuous Mode */ - if (suser()) { - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode |= PROM; /* Set promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } else { - status = -EPERM; - } + if (!suser()) return -EPERM; + while(dev->tbusy) barrier(); /* Stop ring access */ + set_bit(0, (void*)&dev->tbusy); + while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + lp->init_block.mode |= PROM; /* Set promiscuous mode */ + + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ break; + case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */ - if (suser()) { - while(dev->tbusy); /* Stop ring access */ - set_bit(0, (void*)&dev->tbusy); - while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ - - STOP_DEPCA; /* Temporarily stop the depca. */ - depca_init_ring(dev); /* Initialize the descriptor rings */ - lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */ - - LoadCSRs(dev); /* Reload CSR3 */ - InitRestartDepca(dev); /* Resume normal operation. */ - dev->tbusy = 0; /* Unlock the TX ring */ - } else { - status = -EPERM; - } + if (!suser()) return -EPERM; + while(dev->tbusy) barrier(); /* Stop ring access */ + set_bit(0, (void*)&dev->tbusy); + while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */ + STOP_DEPCA; /* Temporarily stop the depca. */ + depca_init_ring(dev); /* Initialize the descriptor rings */ + lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */ + + LoadCSRs(dev); /* Reload CSR3 */ + InitRestartDepca(dev); /* Resume normal operation. */ + dev->tbusy = 0; /* Unlock the TX ring */ break; + case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */ printk("%s: Boo!\n", dev->name); - break; + case DEPCA_GET_MCA: /* Get the multicast address table */ ioc->len = (HASH_TABLE_LEN >> 3); - if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len); - } - + if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT; + memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len); break; - case DEPCA_SET_MCA: /* Set a multicast address */ - if (suser()) { - if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { - memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); - set_multicast_list(dev); - } - } else { - status = -EPERM; - } + case DEPCA_SET_MCA: /* Set a multicast address */ + if (!suser()) return -EPERM; + if (verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len)) return -EFAULT; + memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); + set_multicast_list(dev); break; - case DEPCA_CLR_MCA: /* Clear all multicast addresses */ - if (suser()) { - set_multicast_list(dev); - } else { - status = -EPERM; - } + case DEPCA_CLR_MCA: /* Clear all multicast addresses */ + if (!suser()) return -EPERM; + set_multicast_list(dev); break; + case DEPCA_MCA_EN: /* Enable pass all multicast addressing */ - if (suser()) { + if (!suser()) return -EPERM; set_multicast_list(dev); - } else { - status = -EPERM; - } - break; + case DEPCA_GET_STATS: /* Get the driver statistics */ cli(); ioc->len = sizeof(lp->pktStats); - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); + if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) { + status = -EFAULT; + } else { + memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); } sti(); - break; - case DEPCA_CLR_STATS: /* Zero out the driver statistics */ - if (suser()) { - cli(); - memset(&lp->pktStats, 0, sizeof(lp->pktStats)); - sti(); - } else { - status = -EPERM; - } + case DEPCA_CLR_STATS: /* Zero out the driver statistics */ + if (!suser()) return -EPERM; + cli(); + memset(&lp->pktStats, 0, sizeof(lp->pktStats)); + sti(); break; + case DEPCA_GET_REG: /* Get the DEPCA Registers */ i=0; tmp.sval[i++] = inw(DEPCA_NICSR); @@ -1830,25 +1998,25 @@ tmp.sval[i++] = inw(DEPCA_DATA); memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init)); ioc->len = i+sizeof(struct depca_init); - if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); - } - + if (verify_area(VERIFY_WRITE, ioc->data, ioc->len)) return -EFAULT; + memcpy_tofs(ioc->data, tmp.addr, ioc->len); break; + default: - status = -EOPNOTSUPP; + return -EOPNOTSUPP; } return status; } #ifdef MODULE -static char devicename[9] = { 0, }; +static char devicename[9] = {0,}; static struct device thisDepca = { devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0x200, 7, /* I/O address, IRQ */ - 0, 0, 0, NULL, depca_probe }; + 0, 0, 0, NULL, depca_probe +}; static int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */ static int io=0x200; /* Or use the irq= io= options to insmod */ @@ -1869,8 +2037,13 @@ void cleanup_module(void) { - if (thisDepca.priv) { - kfree(thisDepca.priv); + struct depca_private *lp = thisDepca.priv; + if (lp) { +#ifdef CONFIG_MCA + if(lp->mca_slot != -1) + mca_mark_as_unused(lp->mca_slot); +#endif + kfree(lp); thisDepca.priv = NULL; } thisDepca.irq=0; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- linux.vanilla/drivers/net/eepro100.c Sun Dec 6 00:14:40 1998 +++ linux/drivers/net/eepro100.c Sun Dec 6 03:34:33 1998 @@ -7,7 +7,7 @@ of the GNU Public License, incorporated herein by reference. This driver is for the Intel EtherExpress Pro 100B boards. - It should work with other i82557 boards (if any others exist). + It should work with other i82557 and i82558 boards. To use a built-in driver, install as drivers/net/eepro100.c. To use as a module, use the compile-command at the end of the file. @@ -15,11 +15,13 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771 For updates see - + http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html + There is also a mailing list based at + linux-eepro100@cesdis.gsfc.nasa.gov */ static const char *version = -"eepro100.c:v0.99B 4/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n"; +"eepro100.c:v1.05 10/16/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"; /* A few user-configurable values that apply to all boards. First set are undocumented and spelled per Intel recommendations. */ @@ -41,6 +43,7 @@ /* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */ static int multicast_filter_limit = 64; +#include #ifdef MODULE #ifdef MODVERSIONS #include @@ -53,20 +56,18 @@ #include #include -#include #include #include -#include #include #include #include #include #include -#include -#include /* Processor type for cache alignment. */ +#if LINUX_VERSION_CODE < 0x20155 +#include /* Ignore the bogus warning in 2.1.100+ */ +#endif #include #include -#include #include #include @@ -74,9 +75,9 @@ #include /* Unused in the 2.0.* version, but retained for documentation. */ -#if LINUX_VERSION_CODE > 0x20118 +#if LINUX_VERSION_CODE > 0x20118 && defined(MODULE) MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver"); +MODULE_DESCRIPTION("Intel i82557/i82558 PCI EtherExpressPro driver"); MODULE_PARM(debug, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); @@ -95,6 +96,11 @@ #if (LINUX_VERSION_CODE < 0x20123) #define test_and_set_bit(val, addr) set_bit(val, addr) #endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif /* The total I/O port extent of the board. The registers beyond 0x18 only exist on the i82558. */ @@ -132,7 +138,7 @@ Despite the extra space overhead in each receive skbuff, the driver must use the simplified Rx buffer mode to assure that only a single data buffer is associated with each RxFD. The driver implements this by reserving space -for the Rx descriptor at the head of each Rx skbuff +for the Rx descriptor at the head of each Rx skbuff. The Speedo-3 has receive and command unit base addresses that are added to almost all descriptor pointers. The driver sets these to zero, so that all @@ -147,10 +153,13 @@ The driver must use the complex Tx command+descriptor mode in order to have a indirect pointer to the skbuff data section. Each Tx command block -(TxCB) is associated with a single, immediately appended Tx buffer descriptor +(TxCB) is associated with two immediately appended Tx Buffer Descriptor (TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the speedo_private data structure for each adapter instance. +The newer i82558 explicitly supports this structure, and can read the two +TxBDs in the same PCI burst as the TxCB. + This ring structure is used for all normal transmit packets, but the transmit packet descriptors aren't long enough for most non-Tx commands such as CmdConfigure. This is complicated by the possibility that the chip has @@ -181,10 +190,10 @@ doing the CU_RESUME the chip processes the next-yet-valid post-final-command. So blindly sending a CU_RESUME is only safe if we do it immediately after -erasing the previous CmdSuspend, without the possibility of an intervening -delay. Thus the resume command is always within the interrupts-disabled -region. This is a timing dependence, but handling this condition in a -timing-independent way would considerably complicate the code. +after erasing the previous CmdSuspend, without the possibility of an +intervening delay. Thus the resume command is always within the +interrupts-disabled region. This is a timing dependence, but handling this +condition in a timing-independent way would considerably complicate the code. Note: In previous generation Intel chips, restarting the command unit was a notoriously slow process. This is presumably no longer true. @@ -243,11 +252,11 @@ /* How to wait for the command unit to accept a command. Typically this takes 0 ticks. */ -static inline void wait_for_cmd_done(int cmd_ioaddr) +static inline void wait_for_cmd_done(long cmd_ioaddr) { - short wait = 100; - do ; - while(inb(cmd_ioaddr) && --wait >= 0); + int wait = 100; + do ; + while(inb(cmd_ioaddr) && --wait >= 0); } /* Operational parameter that usually are not changed. */ @@ -306,17 +315,24 @@ u16 size; }; -/* Elements of the RxFD.status word. */ -#define RX_COMPLETE 0x8000 +/* Selected elements of the Tx/RxFD.status word. */ +enum RxFD_bits { + RxComplete=0x8000, RxOK=0x2000, + RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010, + RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002, + StatusComplete=0x8000, +}; struct TxFD { /* Transmit frame descriptor set. */ s32 status; u32 link; /* void * */ u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */ s32 count; /* # of TBD (=1), Tx start thresh., etc. */ - /* This constitutes a single "TBD" entry -- we only use one. */ - u32 tx_buf_addr; /* void *, frame to be transmitted. */ - s32 tx_buf_size; /* Length of Tx frame. */ + /* This constitutes two "TBD" entries -- we only use one. */ + u32 tx_buf_addr0; /* void *, frame to be transmitted. */ + s32 tx_buf_size0; /* Length of Tx frame. */ + u32 tx_buf_addr1; /* void *, frame to be transmitted. */ + s32 tx_buf_size1; /* Length of Tx frame. */ }; /* Elements of the dump_statistics block. This block must be lword aligned. */ @@ -358,10 +374,9 @@ long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - struct descriptor config_cmd; /* A configure command, with header... */ - u8 config_cmd_data[22]; /* .. and setup parameters. */ int mc_setup_frm_len; /* The length of an allocated.. */ struct descriptor *mc_setup_frm; /* ..multicast setup frame. */ + int mc_setup_busy; /* Avoid double-use of setup frame. */ int in_interrupt; /* Word-aligned dev->interrupt */ char rx_mode; /* Current PROMISC/ALLMULTI setting. */ unsigned int tx_full:1; /* The Tx queue is full. */ @@ -376,11 +391,16 @@ /* The parameters for a CmdConfigure operation. There are so many options that it would be difficult to document each bit. We mostly use the default or recommended settings. */ -const char basic_config_cmd[22] = { +const char i82557_config_cmd[22] = { 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ 0, 0x2E, 0, 0x60, 0, 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ 0x3f, 0x05, }; +const char i82558_config_cmd[22] = { + 22, 0x08, 0, 1, 0, 0x80, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0x08, 0x88, + 0x68, 0, 0x40, 0xf2, 0xBD, /* 0xBD->0xFD=Force full-duplex */ + 0x31, 0x05, }; /* PHY media interface chips. */ static const char *phys[] = { @@ -392,12 +412,12 @@ S80C24, I82555, DP83840A=10, }; static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; -static void speedo_found1(struct device *dev, int ioaddr, int irq, +static void speedo_found1(struct device *dev, long ioaddr, int irq, int card_idx); -static int read_eeprom(int ioaddr, int location); -static int mdio_read(int ioaddr, int phy_id, int location); -static int mdio_write(int ioaddr, int phy_id, int location, int value); +static int read_eeprom(long ioaddr, int location, int addr_len); +static int mdio_read(long ioaddr, int phy_id, int location); +static int mdio_write(long ioaddr, int phy_id, int location, int value); static int speedo_open(struct device *dev); static void speedo_timer(unsigned long data); static void speedo_init_rx_ring(struct device *dev); @@ -420,68 +440,91 @@ static int debug = -1; /* The debug level */ #endif +#ifdef honor_default_port +/* Optional driver feature to allow forcing the transceiver setting. + Not recommended. */ +static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100, + 0x2000, 0x2100, 0x0400, 0x3100}; +#endif + /* A list of all installed Speedo devices, for removing the driver module. */ static struct device *root_speedo_dev = NULL; int eepro100_init(struct device *dev) { int cards_found = 0; + static int pci_index = 0; - if (pcibios_present()) { - static int pci_index = 0; - for (; pci_index < 8; pci_index++) { - unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; - int pci_ioaddr; - - unsigned short pci_command, new_command; - - if (pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82557, - pci_index, &pci_bus, - &pci_device_fn)) - break; + if (! pcibios_present()) + return cards_found; + + for (; pci_index < 8; pci_index++) { + unsigned char pci_bus, pci_device_fn, pci_latency; + long ioaddr; + int irq; + + u16 pci_command, new_command; + + if (pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82557, + pci_index, &pci_bus, + &pci_device_fn)) + break; +#if LINUX_VERSION_CODE >= 0x20155 || PCI_SUPPORT_1 + { + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[1]; /* Use [0] to mem-map */ + irq = pdev->irq; + } +#else + { + u32 pci_ioaddr; + u8 pci_irq_line; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_irq_line); /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - if (speedo_debug > 2) - printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n", - (int)pci_ioaddr, pci_irq_line); - - /* Get and check the bus-master and latency values. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 32) { - printk(" PCI latency timer (CFLT) is unreasonably low at %d." - " Setting to 32 clocks.\n", pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 32); - } else if (speedo_debug > 1) - printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); - - speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found); - dev = NULL; - cards_found++; + ioaddr = pci_ioaddr; + irq = pci_irq_line; + } +#endif + /* Remove I/O space marker in bit 0. */ + ioaddr &= ~3; + if (speedo_debug > 2) + printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n", + ioaddr, irq); + + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " device! Updating PCI command %4.4x->%4.4x.\n", + pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { + printk(" PCI latency timer (CFLT) is unreasonably low at %d." + " Setting to 32 clocks.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 32); + } else if (speedo_debug > 1) + printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); + + speedo_found1(dev, ioaddr, irq, cards_found); + dev = NULL; + cards_found++; } return cards_found; } -static void speedo_found1(struct device *dev, int ioaddr, int irq, +static void speedo_found1(struct device *dev, long ioaddr, int irq, int card_idx) { static int did_version = 0; /* Already printed version info. */ @@ -495,7 +538,7 @@ dev = init_etherdev(dev, sizeof(struct speedo_private)); - if (dev->mem_start > 0) + if (dev->mem_start > 0) option = dev->mem_start; else if (card_idx >= 0 && options[card_idx] >= 0) option = options[card_idx]; @@ -508,8 +551,10 @@ { u16 sum = 0; int j; + int addr_len = read_eeprom(ioaddr, 0, 6) == 0xffff ? 8 : 6; + for (j = 0, i = 0; i < 0x40; i++) { - u16 value = read_eeprom(ioaddr, i); + u16 value = read_eeprom(ioaddr, i, addr_len); eeprom[i] = value; sum += value; if (i < 3) { @@ -535,7 +580,7 @@ else product = "Intel EtherExpress Pro 10/100"; - printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr); + printk(KERN_INFO "%s: %s at %#3lx, ", dev->name, product, ioaddr); for (i = 0; i < 5; i++) printk("%2.2X:", dev->dev_addr[i]); @@ -666,22 +711,22 @@ #define eeprom_delay(nanosec) udelay(1); /* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) -#define EE_ERASE_CMD (7 << 6) +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location, int addr_len) { - int i; unsigned short retval = 0; int ee_addr = ioaddr + SCBeeprom; int read_cmd = location | EE_READ_CMD; + int i; outw(EE_ENB & ~EE_CS, ee_addr); outw(EE_ENB, ee_addr); /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { + for (i = 12; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outw(EE_ENB | dataval, ee_addr); eeprom_delay(100); @@ -703,7 +748,7 @@ return retval; } -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(long ioaddr, int phy_id, int location) { int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */ outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); @@ -716,7 +761,7 @@ return val & 0xffff; } -static int mdio_write(int ioaddr, int phy_id, int location, int value) +static int mdio_write(long ioaddr, int phy_id, int location, int value) { int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */ outl(0x04000000 | (location<<16) | (phy_id<<21) | value, @@ -735,7 +780,7 @@ speedo_open(struct device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; #ifdef notdef /* We could reset the chip, but should not need to. */ @@ -752,6 +797,22 @@ MOD_INC_USE_COUNT; + /* Retrigger negotiation to reset previous errors. */ + if ((sp->phy[0] & 0x8000) == 0) { + int phy_addr = sp->phy[0] & 0x1f ; + /* Use 0x3300 for restarting NWay, other values to force xcvr: + 0x0000 10-HD + 0x0100 10-FD + 0x2000 100-HD + 0x2100 100-FD + */ +#ifdef honor_default_port + mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]); +#else + mdio_write(ioaddr, phy_addr, 0, 0x3300); +#endif + } + /* Load the statistics block address. */ wait_for_cmd_done(ioaddr + SCBCmd); outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); @@ -805,6 +866,7 @@ /* Setup the chip and configure the multicast list. */ sp->mc_setup_frm = NULL; sp->mc_setup_frm_len = 0; + sp->mc_setup_busy = 0; sp->rx_mode = -1; /* Invalid -> always reset the mode. */ set_rx_mode(dev); @@ -825,6 +887,9 @@ wait_for_cmd_done(ioaddr + SCBCmd); outw(CU_DUMPSTATS, ioaddr + SCBCmd); + /* No need to wait for the command unit to accept here. */ + if ((sp->phy[0] & 0x8000) == 0) + mdio_read(ioaddr, sp->phy[0] & 0x1f, 0); return 0; } @@ -833,24 +898,22 @@ { struct device *dev = (struct device *)data; struct speedo_private *sp = (struct speedo_private *)dev->priv; - int tickssofar = jiffies - sp->last_rx_time; if (speedo_debug > 3) { - int ioaddr = dev->base_addr; - printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n", + long ioaddr = dev->base_addr; + printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n", dev->name, inw(ioaddr + SCBStatus)); } - if (sp->rx_bug) { - if (tickssofar > 2*HZ || sp->rx_mode < 0) { - /* We haven't received a packet in a Long Time. We might have been - bitten by the receiver hang bug. This can be cleared by sending - a set multicast list command. */ - set_rx_mode(dev); - } - /* We must continue to monitor the media. */ - sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */ - add_timer(&sp->timer); + if (sp->rx_mode < 0 || + (sp->rx_bug && jiffies - sp->last_rx_time > 2*HZ)) { + /* We haven't received a packet in a Long Time. We might have been + bitten by the receiver hang bug. This can be cleared by sending + a set multicast list command. */ + set_rx_mode(dev); } + /* We must continue to monitor the media. */ + sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */ + add_timer(&sp->timer); } /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ @@ -862,30 +925,32 @@ int i; sp->cur_rx = 0; - sp->dirty_rx = RX_RING_SIZE - 1; for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; - skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC); + skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); sp->rx_skbuff[i] = skb; if (skb == NULL) - break; /* Bad news! */ + break; /* OK. Just initially short of Rx bufs. */ skb->dev = dev; /* Mark as being used by this device. */ - rxf = (struct RxFD *)skb->tail; - skb_reserve(skb, sizeof(struct RxFD)); sp->rx_ringp[i] = rxf; + skb_reserve(skb, sizeof(struct RxFD)); if (last_rxf) last_rxf->link = virt_to_bus(rxf); last_rxf = rxf; rxf->status = 0x00000001; /* '1' is flag value only. */ rxf->link = 0; /* None yet. */ /* This field unused by i82557, we use it as a consistency check. */ +#ifdef final_version + rxf->rx_buf_addr = 0xffffffff; +#else rxf->rx_buf_addr = virt_to_bus(skb->tail); - +#endif rxf->count = 0; rxf->size = PKT_BUF_SZ; } + sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); /* Mark the last entry as end-of-list. */ last_rxf->status = 0xC0000002; /* '2' is flag value only. */ sp->last_rxf = last_rxf; @@ -894,20 +959,21 @@ static void speedo_tx_timeout(struct device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; printk(KERN_WARNING "%s: Transmit timed out: status %4.4x " - "command %4.4x.\n", - dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd)); - + " %4.4x at %d/%d command %8.8x.\n", + dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd), + sp->dirty_tx, sp->cur_tx, + sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status); if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) { - printk(KERN_WARNING "%s: Trying to restart the transmitter...\n", - dev->name); - outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), - ioaddr + SCBPointer); - outw(CU_START, ioaddr + SCBCmd); + printk(KERN_WARNING "%s: Trying to restart the transmitter...\n", + dev->name); + outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), + ioaddr + SCBPointer); + outw(CU_START, ioaddr + SCBCmd); } else { - outw(DRVR_INT, ioaddr + SCBCmd); + outw(DRVR_INT, ioaddr + SCBCmd); } /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */ if ((sp->phy[0] & 0x8000) == 0) { @@ -916,6 +982,9 @@ mdio_write(ioaddr, phy_addr, 1, 0x0000); mdio_write(ioaddr, phy_addr, 4, 0x0000); mdio_write(ioaddr, phy_addr, 0, 0x8000); +#ifdef honor_default_port + mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]); +#endif } sp->stats.tx_errors++; dev->trans_start = jiffies; @@ -926,7 +995,7 @@ speedo_start_xmit(struct sk_buff *skb, struct device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int entry; /* Block a timer-based transmit from overlapping. This could better be @@ -963,22 +1032,22 @@ sp->tx_ring[entry].link = virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); sp->tx_ring[entry].tx_desc_addr = - virt_to_bus(&sp->tx_ring[entry].tx_buf_addr); + virt_to_bus(&sp->tx_ring[entry].tx_buf_addr0); /* The data region is always in one buffer descriptor, Tx FIFO threshold of 256. */ sp->tx_ring[entry].count = 0x01208000; - sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data); - sp->tx_ring[entry].tx_buf_size = skb->len; + sp->tx_ring[entry].tx_buf_addr0 = virt_to_bus(skb->data); + sp->tx_ring[entry].tx_buf_size0 = skb->len; /* Todo: perhaps leave the interrupt bit set if the Tx queue is more than half full. Argument against: we should be receiving packets and scavenging the queue. Argument for: if so, it shouldn't matter. */ sp->last_cmd->command &= ~(CmdSuspend | CmdIntr); sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + restore_flags(flags); /* Trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd); - restore_flags(flags); } /* Leave room for set_rx_mode() to fill two entries. */ @@ -998,7 +1067,7 @@ { struct device *dev = (struct device *)dev_instance; struct speedo_private *sp; - int ioaddr, boguscnt = max_interrupt_work; + long ioaddr, boguscnt = max_interrupt_work; unsigned short status; #ifndef final_version @@ -1015,6 +1084,7 @@ if (test_and_set_bit(0, (void*)&sp->in_interrupt)) { printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", dev->name); + sp->in_interrupt = 0; /* Avoid halting machine. */ return; } dev->interrupt = 1; @@ -1058,14 +1128,15 @@ if (speedo_debug > 5) printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n", entry, status); - if ((status & 0x8000) == 0) + if ((status & StatusComplete) == 0) break; /* It still hasn't been processed. */ /* Free the original skb. */ if (sp->tx_skbuff[entry]) { sp->stats.tx_packets++; /* Count only user packets. */ - dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE); + dev_free_skb(sp->tx_skbuff[entry]); sp->tx_skbuff[entry] = 0; - } + } else if ((sp->tx_ring[entry].status&0x70000) == CmdNOp << 16) + sp->mc_setup_busy = 0; dirty_tx++; } @@ -1113,108 +1184,104 @@ struct speedo_private *sp = (struct speedo_private *)dev->priv; int entry = sp->cur_rx % RX_RING_SIZE; int status; + int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx; if (speedo_debug > 4) printk(KERN_DEBUG " In speedo_rx().\n"); /* If we own the next entry, it's a new packet. Send it up. */ - while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) { + while (sp->rx_ringp[entry] != NULL && + (status = sp->rx_ringp[entry]->status) & RxComplete) { + if (--rx_work_limit < 0) + break; if (speedo_debug > 4) printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status, sp->rx_ringp[entry]->count & 0x3fff); - if (status & 0x0200) { - printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, " - "status %8.8x!\n", dev->name, status); - } else if ( ! (status & 0x2000)) { - /* There was a fatal error. This *should* be impossible. */ - sp->stats.rx_errors++; - printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n", - dev->name, status); + if ((status & (RxErrTooBig|RxOK)) != RxOK) { + if (status & RxErrTooBig) + printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, " + "status %8.8x!\n", dev->name, status); + else if ( ! (status & 0x2000)) { + /* There was a fatal error. This *should* be impossible. */ + sp->stats.rx_errors++; + printk(KERN_ERR "%s: Anomalous event in speedo_rx(), " + "status %8.8x.\n", + dev->name, status); + } } else { - /* Malloc up new buffer, compatible with net-2e. */ int pkt_len = sp->rx_ringp[entry]->count & 0x3fff; struct sk_buff *skb; - int rx_in_place = 0; /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ - if (pkt_len > rx_copybreak) { - struct sk_buff *newskb; - char *temp; - - /* Pass up the skb already on the Rx ring. */ - skb = sp->rx_skbuff[entry]; - temp = skb_put(skb, pkt_len); - if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) - printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match" - " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name, - sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp); - /* Get a fresh skbuff to replace the filled one. */ - newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); - - if (newskb) { - struct RxFD *rxf; - rx_in_place = 1; - sp->rx_skbuff[entry] = newskb; - newskb->dev = dev; - rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail; - skb_reserve(newskb, sizeof(struct RxFD)); - /* Unused by i82557, consistency check only. */ - rxf->rx_buf_addr = virt_to_bus(newskb->tail); - rxf->status = 0x00000001; - } else /* No memory, drop the packet. */ - skb = 0; - } else - skb = dev_alloc_skb(pkt_len + 2); - if (skb == NULL) { - int i; - printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name); - /* Check that at least two ring entries are free. - If not, free one and mark stats->rx_dropped++. */ - /* ToDo: This is not correct!!!! We should count the number - of linked-in Rx buffer to very that we have at least two - remaining. */ - for (i = 0; i < RX_RING_SIZE; i++) - if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status) - & RX_COMPLETE)) - break; - - if (i > RX_RING_SIZE -2) { - sp->stats.rx_dropped++; - sp->rx_ringp[entry]->status = 0; - sp->cur_rx++; - } - break; - } - skb->dev = dev; - if (! rx_in_place) { - skb_reserve(skb, 2); /* 16 byte align the data fields */ -#if defined(__i386) && notyet + if (pkt_len < rx_copybreak + && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { + skb->dev = dev; + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + /* 'skb_put()' points to the start of sk_buff data area. */ +#if 1 || USE_IP_CSUM /* Packet is in one chunk -- we can copy + cksum. */ - eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), - pkt_len, 0); + eth_copy_and_sum(skb, + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), + pkt_len, 0); + skb_put(skb, pkt_len); #else memcpy(skb_put(skb, pkt_len), bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); #endif + } else { + void *temp; + /* Pass up the already-filled skbuff. */ + skb = sp->rx_skbuff[entry]; + if (skb == NULL) { + printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n", + dev->name); + break; + } + sp->rx_skbuff[entry] = NULL; + temp = skb_put(skb, pkt_len); + if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) + printk(KERN_ERR "%s: Rx consistency error -- the skbuff " + "addresses do not match in speedo_rx: %p vs. %p " + "/ %p.\n", dev->name, + bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), + skb->head, temp); + sp->rx_ringp[entry] = NULL; } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); sp->stats.rx_packets++; } + entry = (++sp->cur_rx) % RX_RING_SIZE; + } - /* ToDo: This is better than before, but should be checked. */ - { - struct RxFD *rxf = sp->rx_ringp[entry]; - rxf->status = 0xC0000003; /* '3' for verification only */ - rxf->link = 0; /* None yet. */ - rxf->count = 0; - rxf->size = PKT_BUF_SZ; - sp->last_rxf->link = virt_to_bus(rxf); - sp->last_rxf->status &= ~0xC0000000; - sp->last_rxf = rxf; - entry = (++sp->cur_rx) % RX_RING_SIZE; + /* Refill the Rx ring buffers. */ + for (; sp->dirty_rx < sp->cur_rx; sp->dirty_rx++) { + struct RxFD *rxf; + entry = sp->dirty_rx % RX_RING_SIZE; + if (sp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + /* Get a fresh skbuff to replace the consumed one. */ + skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); + sp->rx_skbuff[entry] = skb; + if (skb == NULL) { + sp->rx_ringp[entry] = NULL; + break; /* Better luck next time! */ + } + rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail; + skb->dev = dev; + skb_reserve(skb, sizeof(struct RxFD)); + rxf->rx_buf_addr = virt_to_bus(skb->tail); + } else { + rxf = sp->rx_ringp[entry]; } + rxf->status = 0xC0000001; /* '1' for driver use only. */ + rxf->link = 0; /* None yet. */ + rxf->count = 0; + rxf->size = PKT_BUF_SZ; + sp->last_rxf->link = virt_to_bus(rxf); + sp->last_rxf->status &= ~0xC0000000; + sp->last_rxf = rxf; } sp->last_rx_time = jiffies; @@ -1224,7 +1291,7 @@ static int speedo_close(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct speedo_private *sp = (struct speedo_private *)dev->priv; int i; @@ -1250,7 +1317,7 @@ sp->rx_skbuff[i] = 0; /* Clear the Rx descriptors. */ if (skb) - dev_kfree_skb(skb, FREE_WRITE); + dev_free_skb(skb); } for (i = 0; i < TX_RING_SIZE; i++) { @@ -1258,7 +1325,7 @@ sp->tx_skbuff[i] = 0; /* Clear the Tx descriptors. */ if (skb) - dev_kfree_skb(skb, FREE_WRITE); + dev_free_skb(skb); } if (sp->mc_setup_frm) { kfree(sp->mc_setup_frm); @@ -1303,7 +1370,7 @@ speedo_get_stats(struct device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */ sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs; @@ -1329,7 +1396,7 @@ static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; u16 *data = (u16 *)&rq->ifr_data; int phy = sp->phy[0] & 0x1f; @@ -1362,7 +1429,8 @@ set_rx_mode(struct device *dev) { struct speedo_private *sp = (struct speedo_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; + struct descriptor *last_cmd; char new_rx_mode; unsigned long flags; int entry, i; @@ -1383,57 +1451,55 @@ } if (new_rx_mode != sp->rx_mode) { - /* We must change the configuration. Construct a CmdConfig frame. */ - memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd)); - sp->config_cmd_data[1] = (txfifo << 4) | rxfifo; - sp->config_cmd_data[4] = rxdmacount; - sp->config_cmd_data[5] = txdmacount + 0x80; - sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48; - sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80; - sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05; - if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */ - sp->config_cmd_data[15] |= 0x80; - sp->config_cmd_data[8] = 0; - } - save_flags(flags); + u8 *config_cmd_data; + + save_flags(flags); /* Lock to protect sp->cur_tx. */ cli(); - /* Fill the "real" tx_ring frame with a no-op and point it to us. */ entry = sp->cur_tx++ % TX_RING_SIZE; - sp->tx_skbuff[entry] = 0; /* Nothing to free. */ - sp->tx_ring[entry].status = CmdNOp << 16; - sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd); - sp->config_cmd.status = 0; - sp->config_cmd.command = CmdSuspend | CmdConfigure; - sp->config_cmd.link = - virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); - sp->last_cmd->command &= ~CmdSuspend; - /* Immediately trigger the command unit resume. */ - wait_for_cmd_done(ioaddr + SCBCmd); - outw(CU_RESUME, ioaddr + SCBCmd); - sp->last_cmd = &sp->config_cmd; + last_cmd = sp->last_cmd; + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; restore_flags(flags); - if (speedo_debug > 5) { - int i; - printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry); - for(i = 0; i < 32; i++) - printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]); - printk(".\n"); + + sp->tx_skbuff[entry] = 0; /* Redundant. */ + sp->tx_ring[entry].status = (CmdSuspend | CmdConfigure) << 16; + sp->tx_ring[entry].link = + virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); + config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr; + /* Construct a full CmdConfig frame. */ + memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd)); + config_cmd_data[1] = (txfifo << 4) | rxfifo; + config_cmd_data[4] = rxdmacount; + config_cmd_data[5] = txdmacount + 0x80; + config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0; + config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0; + config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05; + if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */ + config_cmd_data[15] |= 0x80; + config_cmd_data[8] = 0; } + /* Trigger the command unit resume. */ + last_cmd->command &= ~CmdSuspend; + wait_for_cmd_done(ioaddr + SCBCmd); + outw(CU_RESUME, ioaddr + SCBCmd); } - if (new_rx_mode == 0 && dev->mc_count < 3) { - /* The simple case of 0-2 multicast list entries occurs often, and + if (new_rx_mode == 0 && dev->mc_count < 4) { + /* The simple case of 0-3 multicast list entries occurs often, and fits within one tx_ring[] entry. */ - u16 *setup_params, *eaddrs; struct dev_mc_list *mclist; + u16 *setup_params, *eaddrs; - save_flags(flags); + save_flags(flags); /* Lock to protect sp->cur_tx. */ cli(); entry = sp->cur_tx++ % TX_RING_SIZE; + last_cmd = sp->last_cmd; + sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + restore_flags(flags); + sp->tx_skbuff[entry] = 0; sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16; sp->tx_ring[entry].link = - virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); + virt_to_bus(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr; *setup_params++ = dev->mc_count*6; @@ -1446,36 +1512,39 @@ *setup_params++ = *eaddrs++; } - sp->last_cmd->command &= ~CmdSuspend; + last_cmd->command &= ~CmdSuspend; /* Immediately trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd); - sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; - restore_flags(flags); } else if (new_rx_mode == 0) { - /* This does not work correctly, but why not? */ struct dev_mc_list *mclist; - u16 *eaddrs; + u16 *setup_params, *eaddrs; struct descriptor *mc_setup_frm = sp->mc_setup_frm; - u16 *setup_params; int i; if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 || sp->mc_setup_frm == NULL) { - /* Allocate a new frame, 10bytes + addrs, with a few - extra entries for growth. */ + /* Allocate a full setup frame, 10bytes + . */ if (sp->mc_setup_frm) kfree(sp->mc_setup_frm); - sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24; + sp->mc_setup_busy = 0; + sp->mc_setup_frm_len = 10 + multicast_filter_limit*6; sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC); if (sp->mc_setup_frm == NULL) { - printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name); + printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", + dev->name); sp->rx_mode = -1; /* We failed, try again. */ return; } } + /* If we are busy, someone might be quickly adding to the MC list. + Try again later when the list changes stop. */ + if (sp->mc_setup_busy) { + sp->rx_mode = -1; + return; + } mc_setup_frm = sp->mc_setup_frm; - /* Construct the new setup frame. */ + /* Fill the setup frame. */ if (speedo_debug > 1) printk(KERN_DEBUG "%s: Constructing a setup frame at %p, " "%d bytes.\n", @@ -1483,7 +1552,7 @@ mc_setup_frm->status = 0; mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList; /* Link set below. */ - setup_params = (u16 *)mc_setup_frm->params; + setup_params = (u16 *)&mc_setup_frm->params; *setup_params++ = dev->mc_count*6; /* Fill in the multicast addresses. */ for (i = 0, mclist = dev->mc_list; i < dev->mc_count; @@ -1498,10 +1567,10 @@ save_flags(flags); cli(); entry = sp->cur_tx++ % TX_RING_SIZE; - - if (speedo_debug > 5) - printk(" CmdMCSetup frame length %d in entry %d.\n", - dev->mc_count, entry); + last_cmd = sp->last_cmd; + sp->last_cmd = mc_setup_frm; + sp->mc_setup_busy++; + restore_flags(flags); /* Change the command to a NoOp, pointing to the CmdMulti command. */ sp->tx_skbuff[entry] = 0; @@ -1510,17 +1579,15 @@ /* Set the link in the setup frame. */ mc_setup_frm->link = - virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE])); + virt_to_bus(&(sp->tx_ring[(entry+1) % TX_RING_SIZE])); - sp->last_cmd->command &= ~CmdSuspend; + last_cmd->command &= ~CmdSuspend; /* Immediately trigger the command unit resume. */ wait_for_cmd_done(ioaddr + SCBCmd); outw(CU_RESUME, ioaddr + SCBCmd); - sp->last_cmd = mc_setup_frm; - restore_flags(flags); - if (speedo_debug > 1) - printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n", - dev->name, sp->last_cmd, sp->last_cmd->command); + if (speedo_debug > 5) + printk(" CmdMCSetup frame length %d in entry %d.\n", + dev->mc_count, entry); } sp->rx_mode = new_rx_mode; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c --- linux.vanilla/drivers/net/rtl8139.c Sun Dec 6 00:14:40 1998 +++ linux/drivers/net/rtl8139.c Sun Dec 6 03:40:04 1998 @@ -20,11 +20,22 @@ */ static const char *version = -"rtl8139.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n"; +"rtl8139.c:v1.04 9/22/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html\n"; /* A few user-configurable values. */ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 10; +static int max_interrupt_work = 20; +#define rtl8129_debug debug +static int rtl8129_debug = 1; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* Used to pass the full-duplex flag, etc. */ +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; /* Size of the in-memory receive ring. */ #define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ @@ -39,12 +50,13 @@ /* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024. */ #define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ #define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ -#define TX_DMA_BURST 4 +#define TX_DMA_BURST 4 /* Calculate as 16< #ifdef MODULE #ifdef MODVERSIONS #include @@ -60,40 +72,49 @@ #include #include #include -#include #include #include #include #include #include -#include +#include +#include +#include #include /* Processor type for cache alignment. */ #include #include -#include -#include -#include -#include +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ #define RUN_AT(x) (jiffies + (x)) #include -#if (LINUX_VERSION_CODE < 0x20123) +#if LINUX_VERSION_CODE < 0x20123 #define test_and_set_bit(val, addr) set_bit(val, addr) #endif +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif /* The I/O extent. */ #define RTL8129_TOTAL_SIZE 0x80 -#ifdef HAVE_DEVLIST -struct netdev_entry rtl8139_drv = -{"RTL8139", rtl8139_probe, RTL8129_TOTAL_SIZE, NULL}; -#endif - -static int rtl8129_debug = 1; - /* Theory of Operation @@ -139,16 +160,45 @@ IVc. Errata */ + + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; + +static struct device * rtl8129_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chp_idx, int fnd_cnt); + +static struct pci_id_info pci_tbl[] = +{{ "RealTek RTL8129 Fast Ethernet", + 0x10ec, 0x8129, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + { "RealTek RTL8139 Fast Ethernet", + 0x10ec, 0x8139, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + { "RealTek RTL8139 Fast Ethernet (mislabeled)", + 0x1113, 0x1211, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x80, rtl8129_probe1}, + {0,}, /* 0 terminated list. */ +}; + +/* The capability table matches the chip table above. */ +enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04}; +static int rtl_cap_tbl[] = { + HAS_MII_XCVR, HAS_CHIP_XCVR|HAS_LNK_CHNG, HAS_CHIP_XCVR|HAS_LNK_CHNG, +}; -#ifndef PCI_VENDOR_ID_REALTEK -#define PCI_VENDOR_ID_REALTEK 0x10ec -#endif -#ifndef PCI_DEVICE_ID_REALTEK_8129 -#define PCI_DEVICE_ID_REALTEK_8129 0x8129 -#endif -#ifndef PCI_DEVICE_ID_REALTEK_8139 -#define PCI_DEVICE_ID_REALTEK_8139 0x8139 -#endif /* The rest of these values should never change. */ #define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */ @@ -157,7 +207,7 @@ enum RTL8129_registers { MAC0=0, /* Ethernet hardware address. */ MAR0=8, /* Multicast filter. */ - TxStat0=0x10, /* Transmit status (Four 32bit registers). */ + TxStatus0=0x10, /* Transmit status (Four 32bit registers). */ TxAddr0=0x20, /* Tx descriptors (also four 32bit). */ RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36, ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A, @@ -168,7 +218,8 @@ Cfg9346=0x50, Config0=0x51, Config1=0x52, FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B, MultiIntr=0x5C, TxSummary=0x60, - BMCR=0x62, BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, NWayExpansion=0x6A, + MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, + NWayExpansion=0x6A, /* Undocumented registers, but required for proper operation. */ FIFOTMS=0x70, /* FIFO Test Mode Select */ CSCR=0x74, /* Chip Status and Configuration Register. */ @@ -214,24 +265,24 @@ struct device *next_module; int chip_id; int chip_revision; + unsigned char pci_bus, pci_devfn; #if LINUX_VERSION_CODE > 0x20139 struct net_device_stats stats; #else struct enet_statistics stats; #endif struct timer_list timer; /* Media selection timer. */ - unsigned int cur_rx, cur_tx; /* The next free and used entries */ - unsigned int dirty_rx, dirty_tx; + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + unsigned int cur_tx, dirty_tx, tx_flag; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[NUM_TX_DESC]; unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ unsigned char *rx_ring; unsigned char *tx_bufs; /* Tx bounce buffer region. */ - unsigned char mc_filter[8]; /* Current multicast filter. */ char phys[4]; /* MII device addresses. */ - int in_interrupt; /* Alpha needs word-wide lock. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock:1; /* Full-duplex operation requested. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ @@ -239,24 +290,21 @@ }; #ifdef MODULE -/* Used to pass the full-duplex flag, etc. */ -static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -#if LINUX_VERSION_CODE > 0x20118 +#if LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("RealTek RTL8129/8139 Fast Ethernet driver"); -MODULE_PARM(debug, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); #endif #endif -static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, - int chip_id, int options, int card_idx); static int rtl8129_open(struct device *dev); -static int read_eeprom(int ioaddr, int location); -static int mdio_read(int ioaddr, int phy_id, int location); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int val); static void rtl8129_timer(unsigned long data); static void rtl8129_tx_timeout(struct device *dev); static void rtl8129_init_ring(struct device *dev); @@ -264,145 +312,138 @@ static int rtl8129_rx(struct device *dev); static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int rtl8129_close(struct device *dev); +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); static struct enet_statistics *rtl8129_get_stats(struct device *dev); +static inline u32 ether_crc(int length, unsigned char *data); static void set_rx_mode(struct device *dev); -#ifdef MODULE /* A list of all installed RTL8129 devices, for removing the driver module. */ static struct device *root_rtl8129_dev = NULL; -#endif + +/* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well when dynamically adding drivers. So instead we detect just the + Rtl81*9 cards in slot order. */ int rtl8139_probe(struct device *dev) { int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ + int pci_index = 0; + unsigned char pci_bus, pci_device_fn; - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Rtl81*9 cards in slot order. */ - - if (pcibios_present()) { - unsigned char pci_bus, pci_device_fn; - - for (;pci_index < 0xff; pci_index++) { - u8 pci_irq_line, pci_latency; - u16 pci_command, new_command, vendor, device; - u32 pci_ioaddr; + if ( ! pcibios_present()) + return -ENODEV; - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, -#ifdef REVERSE_PROBE_ORDER - 0xff - pci_index, -#else - pci_index, -#endif - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + int chip_idx, irq; + long ioaddr; + + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) break; - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_VENDOR_ID, &vendor); - if (vendor != PCI_VENDOR_ID_REALTEK) - continue; + if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ + continue; - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_DEVICE_ID, &device); + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; +#else + u32 pci_ioaddr; + u8 pci_irq_line; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_irq_line); pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr); - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - - if (device != PCI_DEVICE_ID_REALTEK_8129 - && device != PCI_DEVICE_ID_REALTEK_8139) { - printk(KERN_NOTICE "Unknown RealTek PCI ethernet chip type " - "%4.4x detected: not configured.\n", device); - continue; - } - if (check_region(pci_ioaddr, RTL8129_TOTAL_SIZE)) - continue; + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; +#endif + } - /* Activate the card: fix for brain-damaged Win98 BIOSes. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI config %4.4x->%4.4x.\n", - pci_command, new_command); - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && + check_region(ioaddr, pci_tbl[chip_idx].io_size)) + continue; + + /* Activate the card: fix for brain-damaged Win98 BIOSes. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | (pci_tbl[chip_idx].flags & 7); + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } -#ifdef MODULE - dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device, - options[cards_found], cards_found); -#else - dev = rtl8129_probe1(dev, pci_ioaddr, pci_irq_line, device, - dev ? dev->mem_start : 0, -1); -#endif + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); - if (dev) { - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 32) { - printk(KERN_NOTICE" PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to 64 clocks.\n", - pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 64); - } else if (rtl8129_debug > 1) - printk(KERN_INFO" PCI latency timer (CFLT) is %#x.\n", - pci_latency); - dev = 0; - cards_found++; + if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { + u8 pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { + printk(KERN_NOTICE " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 64); } } + dev = 0; + cards_found++; } -#if defined (MODULE) - return cards_found; -#else return cards_found ? 0 : -ENODEV; -#endif } -static struct device *rtl8129_probe1(struct device *dev, int ioaddr, int irq, - int chip_id, int options, int card_idx) +static struct device * rtl8129_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, + int irq, int chip_idx, int found_cnt) { static int did_version = 0; /* Already printed version info. */ struct rtl8129_private *tp; - int i; + int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0; if (rtl8129_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); - printk(KERN_INFO "%s: RealTek RTL%x at %#3x, IRQ %d, ", - dev->name, chip_id, ioaddr, irq); + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", + dev->name, pci_tbl[chip_idx].name, ioaddr, irq); /* Bring the chip out of low-power mode. */ outb(0x00, ioaddr + Config1); - /* Perhaps this should be read from the EEPROM? */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = inb(ioaddr + MAC0 + i); + if (read_eeprom(ioaddr, 0) != 0xffff) + for (i = 0; i < 3; i++) + ((u16 *)(dev->dev_addr))[i] = read_eeprom(ioaddr, i + 7); + else + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(ioaddr + MAC0 + i); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x.\n", dev->dev_addr[i]); - if (rtl8129_debug > 1) { - printk(KERN_INFO "%s: EEPROM contents\n", dev->name); - for (i = 0; i < 64; i++) - printk(" %4.4x%s", read_eeprom(ioaddr, i), - i%16 == 15 ? "\n"KERN_INFO : ""); - } - /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, RTL8129_TOTAL_SIZE, "RealTek RTL8129/39 Fast Ethernet"); + request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; @@ -412,22 +453,21 @@ memset(tp, 0, sizeof(*tp)); dev->priv = tp; -#ifdef MODULE tp->next_module = root_rtl8129_dev; root_rtl8129_dev = dev; -#endif - tp->chip_id = chip_id; + tp->chip_id = chip_idx; + tp->pci_bus = pci_bus; + tp->pci_devfn = pci_devfn; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes too much time. */ - if (chip_id == 0x8129) { + if (rtl_cap_tbl[chip_idx] & HAS_MII_XCVR) { int phy, phy_idx; for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(ioaddr, phy, 1); - + int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { tp->phys[phy_idx++] = phy; printk(KERN_INFO "%s: MII transceiver found at address %d.\n", @@ -441,7 +481,7 @@ tp->phys[0] = -1; } } else { - tp->phys[0] = -1; + tp->phys[0] = 32; } /* Put the chip into low-power mode. */ @@ -450,18 +490,21 @@ outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ /* The lower four bits are the media type. */ - if (options > 0) { - tp->full_duplex = (options & 16) ? 1 : 0; - tp->default_port = options & 15; + if (option > 0) { + tp->full_duplex = (option & 0x200) ? 1 : 0; + tp->default_port = option & 15; if (tp->default_port) tp->medialock = 1; } -#ifdef MODULE - if (card_idx >= 0) { - if (full_duplex[card_idx] >= 0) - tp->full_duplex = full_duplex[card_idx]; + + if (found_cnt < MAX_UNITS && full_duplex[found_cnt] > 0) + tp->full_duplex = full_duplex[found_cnt]; + + if (tp->full_duplex) { + printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + mdio_write(dev, tp->phys[0], 4, 0x141); + tp->duplex_lock = 1; } -#endif /* The Rtl8129-specific entries in the device structure. */ dev->open = &rtl8129_open; @@ -469,6 +512,7 @@ dev->stop = &rtl8129_close; dev->get_stats = &rtl8129_get_stats; dev->set_multicast_list = &set_rx_mode; + dev->do_ioctl = &mii_ioctl; return dev; } @@ -485,25 +529,21 @@ #define EE_ENB (0x80 | EE_CS) /* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz is untested. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. */ -#ifdef _LINUX_DELAY_H -#define eeprom_delay(nanosec) udelay(1) -#else -#define eeprom_delay(nanosec) do { ; } while (0) -#endif +#define eeprom_delay() inl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) #define EE_READ_CMD (6 << 6) #define EE_ERASE_CMD (7 << 6) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location) { int i; unsigned retval = 0; - int ee_addr = ioaddr + Cfg9346; + long ee_addr = ioaddr + Cfg9346; int read_cmd = location | EE_READ_CMD; outb(EE_ENB & ~EE_CS, ee_addr); @@ -513,20 +553,19 @@ for (i = 10; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(EE_ENB | dataval, ee_addr); - eeprom_delay(100); + eeprom_delay(); outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - outb(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); + eeprom_delay(); } outb(EE_ENB, ee_addr); + eeprom_delay(); for (i = 16; i > 0; i--) { outb(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + eeprom_delay(); retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); outb(EE_ENB, ee_addr); - eeprom_delay(100); + eeprom_delay(); } /* Terminate the EEPROM access. */ @@ -544,38 +583,42 @@ #define MDIO_DATA_OUT 0x04 #define MDIO_DATA_IN 0x02 #define MDIO_CLK 0x01 -#ifdef _LINUX_DELAY_H -#define mdio_delay() udelay(1) /* Really 400ns. */ -#else -#define mdio_delay() do { ; } while (0) -#endif +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() inb(mdio_addr) + +static char mii_2_8139_map[8] = {MII_BMCR, MII_BMSR, 0, 0, NWayAdvert, + NWayLPAR, NWayExpansion, 0 }; /* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync(int ioaddr) +static void mdio_sync(long mdio_addr) { int i; - int mdio_addr = ioaddr + MII_SMI; for (i = 32; i >= 0; i--) { - outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr); + outb(MDIO_WRITE1, mdio_addr); mdio_delay(); - outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr); + outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr); mdio_delay(); } return; } -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(struct device *dev, int phy_id, int location) { - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + long mdio_addr = dev->base_addr + MII_SMI; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; - int mdio_addr = ioaddr + MII_SMI; + int i; - mdio_sync(ioaddr); + if ((phy_id & 0x1f) == 0) { /* Really a 8139. Use internal registers. */ + return location < 8 && mii_2_8139_map[location] ? + inw(dev->base_addr + mii_2_8139_map[location]) : 0; + } + mdio_sync(mdio_addr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { - int dataval = - (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; outb(MDIO_DIR | dataval, mdio_addr); mdio_delay(); @@ -593,14 +636,45 @@ } return (retval>>1) & 0xffff; } + +static void mdio_write(struct device *dev, int phy_id, int location, int value) +{ + long mdio_addr = dev->base_addr + MII_SMI; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (phy_id == 32) { /* Really a 8139. Use internal registers. */ + if (location < 8 && mii_2_8139_map[location]) + outw(value, dev->base_addr + mii_2_8139_map[location]); + return; + } + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + outb(dataval, mdio_addr); + mdio_delay(); + outb(dataval | MDIO_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outb(0, mdio_addr); + mdio_delay(); + outb(MDIO_CLK, mdio_addr); + mdio_delay(); + } + return; +} + static int rtl8129_open(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i; - int full_duplex = 0; /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); @@ -636,29 +710,26 @@ outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); + tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000; - full_duplex = tp->full_duplex; - if (tp->phys[0] >= 0 || tp->chip_id == 0x8139) { - u16 mii_reg5; - if (tp->chip_id == 0x8139) - mii_reg5 = inw(ioaddr + NWayLPAR); - else - mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); + tp->full_duplex = tp->duplex_lock; + if (tp->phys[0] >= 0 || (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR)) { + u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5); if (mii_reg5 == 0xffff) ; /* Not there */ else if ((mii_reg5 & 0x0100) == 0x0100 || (mii_reg5 & 0x00C0) == 0x0040) - full_duplex = 1; + tp->full_duplex = 1; if (rtl8129_debug > 1) printk(KERN_INFO"%s: Setting %s%s-duplex based on" " auto-negotiated partner ability %4.4x.\n", dev->name, mii_reg5 == 0 ? "" : (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ", - full_duplex ? "full" : "half", mii_reg5); + tp->full_duplex ? "full" : "half", mii_reg5); } outb(0xC0, ioaddr + Cfg9346); - outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); outb(0x00, ioaddr + Cfg9346); outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf); @@ -678,10 +749,10 @@ | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); if (rtl8129_debug > 1) - printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d" + printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d" " GP Pins %2.2x %s-duplex.\n", dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), - full_duplex ? "full" : "half"); + tp->full_duplex ? "full" : "half"); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ @@ -698,26 +769,26 @@ { struct device *dev = (struct device *)data; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int next_tick = 0; + int mii_reg5 = mdio_read(dev, tp->phys[0], 5); - if (tp->chip_id == 0x8139) { - u16 mii_reg5 = inw(ioaddr + NWayLPAR); - if ((mii_reg5 & 0x0100) == 0x0100 - || (mii_reg5 & 0x00C0) == 0x0040) - if ( ! tp->full_duplex) { - tp->full_duplex = 1; - if (rtl8129_debug > 0) - printk(KERN_INFO "%s: Switching to full-duplex based on " - "link partner ability of %4.4x.\n", - dev->name, mii_reg5); - outb(0xC0, ioaddr + Cfg9346); - outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); - outb(0x00, ioaddr + Cfg9346); - } + if (! tp->duplex_lock && mii_reg5 != 0xffff) { + int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040; + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" + " partner ability of %4.4x.\n", dev->name, + tp->full_duplex ? "full" : "half", tp->phys[0], mii_reg5); + outb(0xC0, ioaddr + Cfg9346); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(0x00, ioaddr + Cfg9346); + } + next_tick = 60*HZ; } + if (rtl8129_debug > 2) { - if (tp->chip_id == 0x8129) + if (rtl_cap_tbl[tp->chip_id] & HAS_MII_XCVR) printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", dev->name, inb(ioaddr + GPPinData)); else @@ -740,41 +811,51 @@ static void rtl8129_tx_timeout(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; - int i; + long ioaddr = dev->base_addr; + int mii_reg, i; + + if (rtl8129_debug > 0) + printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x " + "media %2.2x.\n", + dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus), + inb(ioaddr + GPPinData)); /* Disable interrupts by clearing the interrupt mask. */ outw(0x0000, ioaddr + IntrMask); - - if (rtl8129_debug > 0) - printk(KERN_WARNING "%s: Transmit timeout, status %2.2x %4.4x.\n", - dev->name, inb(ioaddr + ChipCmd), inw(ioaddr + IntrStatus)); /* Emit info to figure out what went wrong. */ + printk("%s: Tx queue start entry %d dirty entry %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); for (i = 0; i < NUM_TX_DESC; i++) printk(KERN_DEBUG"%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, inl(ioaddr + TxStat0 + i*4), + dev->name, i, inl(ioaddr + TxStatus0 + i*4), i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); - if (tp->chip_id == 0x8129) { - int mii_reg; - printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); - for (mii_reg = 0; mii_reg < 8; mii_reg++) - printk(" %4.4x", mdio_read(ioaddr, tp->phys[0], mii_reg)); - printk(".\n"); - } else { - printk(KERN_DEBUG"%s: MII status register is %4.4x.\n", - dev->name, inw(ioaddr + BMSR)); - } + printk(KERN_DEBUG"%s: MII #%d registers are:", dev->name, tp->phys[0]); + for (mii_reg = 0; mii_reg < 8; mii_reg++) + printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); + printk(".\n"); /* Soft reset the chip. */ outb(CmdReset, ioaddr + ChipCmd); + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) + if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) + break; for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + MAC0 + i); + outb(0x00, ioaddr + Cfg9346); + tp->cur_rx = 0; + /* Must enable Tx/Rx before setting transfer thresholds! */ + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), + ioaddr + RxConfig); + outl((TX_DMA_BURST<<8), ioaddr + TxConfig); + set_rx_mode(dev); { /* Save the unsent Tx packets. */ struct sk_buff *saved_skb[NUM_TX_DESC], *skb; - int j = 0; - for (; tp->cur_tx - tp->dirty_tx > 0 ; tp->dirty_tx++) - saved_skb[j++] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; + int j; + for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) + saved_skb[j] = tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < j; i++) { @@ -785,27 +866,20 @@ } else outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + i*4); /* Note: the chip doesn't have auto-pad! */ - outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | - (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), - ioaddr + TxStat0 + i*4); + outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), + ioaddr + TxStatus0 + i*4); } tp->cur_tx = i; while (i < NUM_TX_DESC) tp->tx_skbuff[i] = 0; if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ dev->tbusy = 0; + tp->tx_full = 0; } else { tp->tx_full = 1; } } - /* Must enable Tx/Rx before setting transfer thresholds! */ - set_rx_mode(dev); - outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); - outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8), - ioaddr + RxConfig); - outl((TX_DMA_BURST<<8), ioaddr + TxConfig); - dev->trans_start = jiffies; tp->stats.tx_errors++; /* Enable all known interrupts by setting the interrupt mask. */ @@ -823,8 +897,8 @@ int i; tp->tx_full = 0; - tp->cur_rx = tp->cur_tx = 0; - tp->dirty_rx = tp->dirty_tx = 0; + tp->cur_rx = 0; + tp->dirty_tx = tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { tp->tx_skbuff[i] = 0; @@ -836,7 +910,7 @@ rtl8129_start_xmit(struct sk_buff *skb, struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int entry; /* Block a timer-based transmit from overlapping. This could better be @@ -858,20 +932,19 @@ } else outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4); /* Note: the chip doesn't have auto-pad! */ - outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | - (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), - ioaddr + TxStat0 + entry*4); + outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), + ioaddr + TxStatus0 + entry*4); if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); } else { tp->tx_full = 1; } dev->trans_start = jiffies; if (rtl8129_debug > 4) - printk(KERN_DEBUG"%s: Queued Tx packet at %p size %ld to slot %d.\n", - dev->name, skb->data, skb->len, entry); + printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n", + dev->name, skb->data, (int)skb->len, entry); return 0; } @@ -881,23 +954,26 @@ static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct device *dev = (struct device *)dev_instance; - struct rtl8129_private *tp; - int ioaddr, boguscnt = max_interrupt_work; + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int boguscnt = max_interrupt_work; int status; + long ioaddr = dev->base_addr; - if (dev == NULL) { - printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n", - irq); +#if defined(__i386__) + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); + dev->interrupt = 0; /* Avoid halting machine. */ return; } - - ioaddr = dev->base_addr; - tp = (struct rtl8129_private *)dev->priv; - if (test_and_set_bit(0, (void*)&tp->in_interrupt)) { +#else + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1; +#endif do { status = inw(ioaddr + IntrStatus); @@ -920,9 +996,9 @@ for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; dirty_tx++) { int entry = dirty_tx % NUM_TX_DESC; - int txstatus = inl(ioaddr + TxStat0 + entry*4); + int txstatus = inl(ioaddr + TxStatus0 + entry*4); - if ( ! (txstatus & TxHostOwns)) + if ( ! (txstatus & (TxStatOK | TxUnderrun | TxAborted))) break; /* It still hasn't been Txed */ /* Note: TxCarrierLost is always asserted at 100mbps. */ @@ -949,7 +1025,9 @@ /* No count for tp->stats.tx_deferred */ #endif if (txstatus & TxUnderrun) { - /* Todo: increase the Tx FIFO threshold. */ + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; tp->stats.tx_fifo_errors++; } tp->stats.collisions += (txstatus >> 24) & 15; @@ -960,7 +1038,7 @@ } /* Free the original skb. */ - dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); + dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } @@ -972,8 +1050,7 @@ } #endif - if (tp->tx_full && dev->tbusy - && dirty_tx > tp->cur_tx - NUM_TX_DESC) { + if (tp->tx_full && dirty_tx > tp->cur_tx - NUM_TX_DESC) { /* The ring is no longer full, clear tbusy. */ tp->tx_full = 0; dev->tbusy = 0; @@ -986,10 +1063,29 @@ /* Check uncommon events with one test. */ if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver |TxErr|RxErr)) { + if (rtl8129_debug > 2) + printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n", + dev->name, status); + + if (status == 0xffffffff) + break; /* Update the error count. */ tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); outl(0, ioaddr + RxMissed); + if ((status & RxUnderrun) && + (rtl_cap_tbl[tp->chip_id] & HAS_LNK_CHNG)) { + /* Really link-change on new chips. */ + int lpar = inw(ioaddr + NWayLPAR); + int duplex = (lpar&0x0100)||(lpar & 0x01C0) == 0x0040; + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + outb(0xC0, ioaddr + Cfg9346); + outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(0x00, ioaddr + Cfg9346); + } + status &= ~RxUnderrun; + } if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++; @@ -1000,7 +1096,14 @@ tp->cur_rx = inw(ioaddr + RxBufAddr) % RX_BUF_LEN; outw(tp->cur_rx - 16, ioaddr + RxBufPtr); } - /* Error sources cleared above. */ + if (status & PCIErr) { + u32 pci_cmd_status; + pcibios_read_config_dword(tp->pci_bus, tp->pci_devfn, + PCI_COMMAND, &pci_cmd_status); + + printk(KERN_ERR "%s: PCI Bus error %4.4x.\n", + dev->name, pci_cmd_status); + } } if (--boguscnt < 0) { printk(KERN_WARNING"%s: Too much work at interrupt, " @@ -1016,18 +1119,20 @@ printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, inl(ioaddr + IntrStatus)); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; - clear_bit(0, (void*)&tp->in_interrupt); +#endif return; } /* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the field alignments and semantics. */ -static int -rtl8129_rx(struct device *dev) +static int rtl8129_rx(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; unsigned char *rx_ring = tp->rx_ring; u16 cur_rx = tp->cur_rx; @@ -1103,9 +1208,16 @@ printk(".\n"); memset(rx_ring, 0xcc, 16); } - } else + } else { +#if 1 /* USE_IP_COPYSUM */ + eth_copy_and_sum(skb, &rx_ring[ring_offset + 4], + rx_size, 0); + skb_put(skb, rx_size); +#else memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4], rx_size); +#endif + } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); #if LINUX_VERSION_CODE > 0x20119 @@ -1114,8 +1226,7 @@ tp->stats.rx_packets++; } - cur_rx += rx_size + 4; - cur_rx = (cur_rx + 3) & ~3; + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; outw(cur_rx - 16, ioaddr + RxBufPtr); } if (rtl8129_debug > 4) @@ -1130,7 +1241,7 @@ static int rtl8129_close(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; int i; @@ -1157,7 +1268,7 @@ for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) - dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); + dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } kfree(tp->rx_ring); @@ -1173,11 +1284,33 @@ return 0; } +static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + u16 *data = (u16 *)&rq->ifr_data; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = tp->phys[0] & 0x3f; + /* Fall Through */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + static struct enet_statistics * rtl8129_get_stats(struct device *dev) { struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (dev->start) { tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); @@ -1188,88 +1321,69 @@ } /* Set or clear the multicast filter for this adaptor. - Note that we only use exclusion around actually queueing the - new frame, not around filling tp->setup_frame. This is non-deterministic - when re-entered but still correct. */ - -/* The little-endian AUTODIN II ethernet CRC calculation. - N.B. Do not use for bulk data, use a table-based routine instead. - This is common code and should be moved to net/core/crc.c */ -static unsigned const ethernet_polynomial_le = 0xedb88320U; -static inline unsigned ether_crc_le(int length, unsigned char *data) + This routine is not state sensitive and need not be SMP locked. */ + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) { - unsigned int crc = 0xffffffff; /* Initial value. */ - while(--length >= 0) { + int crc = -1; + + while(--length >= 0) { unsigned char current_octet = *data++; int bit; - for (bit = 8; --bit >= 0; current_octet >>= 1) { - if ((crc ^ current_octet) & 1) { - crc >>= 1; - crc ^= ethernet_polynomial_le; - } else - crc >>= 1; - } - } - return crc; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; } +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, + AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01, +}; + static void set_rx_mode(struct device *dev) { - int ioaddr = dev->base_addr; - struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; - unsigned char mc_filter[8]; /* Multicast hash filter */ - int i; + long ioaddr = dev->base_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int i, rx_mode; - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + if (rtl8129_debug > 3) + printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", + dev->name, dev->flags, inl(ioaddr + RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { /* Unconditionally log net taps. */ printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(0x0F, ioaddr + RxConfig); - } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + rx_mode = AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((dev->mc_count > multicast_filter_limit) + || (dev->flags & IFF_ALLMULTI)) { /* Too many to filter perfectly -- accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(0x0E, ioaddr + RxConfig); - } else if (dev->mc_count == 0) { - outb(0x0A, ioaddr + RxConfig); - return; + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; } else { struct dev_mc_list *mclist; - - memset(mc_filter, 0, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, - mc_filter); + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); } - /* ToDo: perhaps we need to stop the Tx and Rx process here? */ - if (memcmp(mc_filter, tp->mc_filter, sizeof(mc_filter))) { - for (i = 0; i < 2; i++) - outl(((u32 *)mc_filter)[i], ioaddr + MAR0 + i*4); - memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter)); - } - if (rtl8129_debug > 3) - printk(KERN_DEBUG"%s: set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", - dev->name, dev->flags, inl(ioaddr + RxConfig)); + /* We can safely update without stopping the chip. */ + outb(rx_mode, ioaddr + RxConfig); + outl(mc_filter[0], ioaddr + MAR0 + 0); + outl(mc_filter[1], ioaddr + MAR0 + 4); return; } #ifdef MODULE - -/* An additional parameter that may be passed in... */ -static int debug = -1; - -int -init_module(void) +int init_module(void) { - int cards_found; - - if (debug >= 0) - rtl8129_debug = debug; - - root_rtl8129_dev = NULL; - cards_found = rtl8139_probe(0); - - return cards_found ? 0 : -ENODEV; + return rtl8139_probe(0); } void @@ -1279,9 +1393,13 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_rtl8129_dev) { - next_dev = ((struct rtl8129_private *)root_rtl8129_dev->priv)->next_module; + struct rtl8129_private *tp = + (struct rtl8129_private *)root_rtl8129_dev->priv; + next_dev = tp->next_module; unregister_netdev(root_rtl8129_dev); - release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE); + release_region(root_rtl8129_dev->base_addr, + pci_tbl[tp->chip_id].io_size); + kfree(tp); kfree(root_rtl8129_dev); root_rtl8129_dev = next_dev; } diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/skeleton.c linux/drivers/net/skeleton.c --- linux.vanilla/drivers/net/skeleton.c Sun Jun 21 18:41:04 1998 +++ linux/drivers/net/skeleton.c Wed Dec 9 20:02:31 1998 @@ -362,15 +362,6 @@ dev->trans_start = jiffies; } /* - * If some higher layer thinks we've missed an tx-done interrupt - * we are passed NULL. Caution: dev_tint() handles the cli()/sti() - * itself. - */ - if (skb == NULL) { - dev_tint(dev); - return 0; - } - /* * Block a timer-based transmit from overlapping. This could better be * done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/net/tulip.c linux/drivers/net/tulip.c --- linux.vanilla/drivers/net/tulip.c Sun Dec 6 00:14:40 1998 +++ linux/drivers/net/tulip.c Fri Dec 11 20:18:43 1998 @@ -5,9 +5,9 @@ This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the Digital "Tulip" ethernet adapter interface. + This driver is for the Digital "Tulip" Ethernet adapter interface. It should work with most DEC 21*4*-based chips/ethercards, as well as - PNIC and MXIC chips. + with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences @@ -18,7 +18,7 @@ */ #define SMP_CHECK -static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; +static const char version[] = "tulip.c:v0.90 10/20/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ @@ -63,14 +63,20 @@ /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (4*HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 #include +#include #ifdef MODULE #ifdef MODVERSIONS #include #endif #include -#include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT @@ -80,87 +86,65 @@ #include #include #include -#include #include #include #include #include #include -#include -#include /* Processor type for cache alignment. */ -#include -#include -#include - #include #include #include +#include /* Processor type for cache alignment. */ +#include +#include -/* Kernel compatibility defines, common to David Hind's PCMCIA package. +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. This is only in the support-all-kernels source code. */ -#include /* Evil, but neccessary */ - -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 -#define RUN_AT(x) (x) /* What to put in timer->expires. */ -#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#define virt_to_bus(addr) ((unsigned long)addr) -#define bus_to_virt(addr) ((void*)addr) -#else /* 1.3.0 and later */ -#define RUN_AT(x) (jiffies + (x)) -#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); #endif -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 -#ifdef MODULE -#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) -char kernel_version[] = UTS_RELEASE; -#endif -#else -#undef MOD_INC_USE_COUNT -#define MOD_INC_USE_COUNT -#undef MOD_DEC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif -#endif /* 1.3.38 */ +#define RUN_AT(x) (jiffies + (x)) -#if (LINUX_VERSION_CODE >= 0x10344) -#define NEW_MULTICAST -#include -#endif #if (LINUX_VERSION_CODE >= 0x20100) char kernel_version[] = UTS_RELEASE; #endif -#ifdef SA_SHIRQ -#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) -#else -#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) -#endif -#if (LINUX_VERSION_CODE < 0x20123) +#if LINUX_VERSION_CODE < 0x20123 #define hard_smp_processor_id() smp_processor_id() #define test_and_set_bit(val, addr) set_bit(val, addr) #endif - -/* This my implementation of shared IRQs, now only used for 1.2.13. */ -#ifdef HAVE_SHARED_IRQ -#define USE_SHARED_IRQ -#include +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 #endif - -/* The total size is unusually large: The 21040 aligns each of its 16 - longword-wide registers on a quadword boundary. */ -#define TULIP_TOTAL_SIZE 0x80 - -#ifdef HAVE_DEVLIST -struct netdev_entry tulip_drv = -{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); #endif +#define tulip_debug debug #ifdef TULIP_DEBUG -int tulip_debug = TULIP_DEBUG; +static int tulip_debug = TULIP_DEBUG; #else -int tulip_debug = 1; +static int tulip_debug = 1; #endif /* @@ -170,17 +154,27 @@ This device driver is designed for the DECchip "Tulip", Digital's single-chip ethernet controllers for PCI. Supported members of the family -are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on -many PCI boards including the SMC EtherPower series. - +are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike +chips from Lite-On, Macronics, ASIX, Compex and other listed below are also +supported. + +These chips are used on at least 140 unique PCI board designs. The great +number of chips and board designs supported is the reason for the +driver size and complexity. Almost of the increasing complexity is in the +board configuration and media selection code. There is very little +increasing in the operational critical path length. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The system BIOS preferably should assign the PCI INTA signal to an otherwise unused system IRQ line. -Note: Kernel versions earlier than 1.3.73 do not support shared PCI -interrupt lines. + +Some boards have EEPROMs tables with default media entry. The factory default +is usually "autoselect". This should only be overridden when using +transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) +for forcing full-duplex when used with old link partners that do not do +autonegotiation. III. Driver operation @@ -228,7 +222,9 @@ IV. Notes -Thanks to Duke Kamstra of SMC for providing an EtherPower board. +Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. +Greg LaPolla at Linksys provided PNIC and other Linksys boards. +Znyx provided a four-port card for testing. IVb. References @@ -238,88 +234,105 @@ IVc. Errata -The DEC databook doesn't document which Rx filter settings accept broadcast -packets. Nor does it document how to configure the part to configure the -serial subsystem for normal (vs. loopback) operation or how to have it -autoswitch between internal 10baseT, SIA and AUI transceivers. - +The old DEC databooks were light on details. The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? */ +register of the set CSR12-15 written. Hmmm, now how is that possible? +The DEC SROM format is very badly designed not precisely defined, leading to +part of the media selection junkheap below. Some boards do not have EEPROM +media tables and need to be patched up. Worse, other boards use the DEC +design kit media table when it isn't correct for their board. -/* A few values that may be tweaked. */ -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +We cannot use MII interrupts because there is no defined GPIO pin to attach +them. -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 +*/ #ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ #define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ -#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ -#endif - -#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS -#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ -#endif -#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 -#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 #endif -#ifndef PCI_VENDOR_ID_LITEON -#define PCI_VENDOR_ID_LITEON 0x11AD -#endif +static struct device * +tulip_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr, + int irq, int chip_idx, int board_idx); + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +#define PCI_ADDR0_IO (PCI_USES_IO|PCI_ADDR0) -#ifndef PCI_VENDOR_ID_MXIC -#define PCI_VENDOR_ID_MXIC 0x10d9 -#define PCI_DEVICE_ID_MX98713 0x0512 -#define PCI_DEVICE_ID_MX98715 0x0531 -#define PCI_DEVICE_ID_MX98725 0x0531 -#endif +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size, min_latency; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; +#ifndef CARDBUS +static struct pci_id_info pci_tbl[] = { + { "Digital DC21040 Tulip", + 0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DC21041 Tulip", + 0x1011, 0x0014, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21140 Tulip", + 0x1011, 0x0009, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21143 Tulip", + 0x1011, 0x0019, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Lite-On 82c168 PNIC", + 0x11AD, 0x0002, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98713 PMAC", + 0x10d9, 0x0512, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98715 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98725 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "ASIX AX88140", + 0x125B, 0x1400, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + {0}, +}; +#endif CARD_BUS -/* The rest of these values should never change. */ +/* This table use during operation for capabilities and media timer. */ static void tulip_timer(unsigned long data); static void t21142_timer(unsigned long data); static void mxic_timer(unsigned long data); static void pnic_timer(unsigned long data); -/* A table describing the chip types. */ -enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; +enum tbl_flag { + HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, + HAS_ACPI=0x10, +}; static struct tulip_chip_table { - int vendor_id, device_id; char *chip_name; int io_size; int valid_intrs; /* CSR7 interrupt enable settings */ int flags; void (*media_timer)(unsigned long data); } tulip_tbl[] = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, - "Digital DS21140 Tulip", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, - tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, - "Digital DS21142/3 Tulip", 256, 0x0801fbff, - HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, - { PCI_VENDOR_ID_LITEON, 0x0002, - "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, - "Macronix 98713 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, - "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, - "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, + { "Digital DS21140 Tulip", 128, 0x0001ebef, HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, - {0, 0, 0, 0}, + { "Digital DS21143 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI, t21142_timer }, + { "Lite-On 82c168 PNIC", 256, 0x0001ebef, + HAS_MII, pnic_timer }, + { "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, + {0}, }; /* This matches the table above. */ enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, @@ -367,6 +380,13 @@ u32 buffer1, buffer2; /* We use only buffer 1. */ }; +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. +*/ +#define DESC_RING_WRAP 0x02000000 + struct medialeaf { u8 type; u8 media; @@ -401,11 +421,7 @@ u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ int chip_id; int revision; -#if LINUX_VERSION_CODE > 0x20139 struct net_device_stats stats; -#else - struct enet_statistics stats; -#endif struct timer_list timer; /* Media selection timer. */ int interrupt; /* In-interrupt flag. */ #ifdef SMP_CHECK @@ -428,13 +444,11 @@ signed char phys[4], mii_cnt; /* MII device addresses. */ struct mediatable *mtable; int cur_index; /* Current media index. */ - unsigned char pci_bus, pci_dev_fn; + int saved_if_port; + unsigned char pci_bus, pci_devfn; int pad0, pad1; /* Used for 8-byte alignment */ }; -static struct device *tulip_probe1(int pci_bus, int pci_devfn, - struct device *dev, - int chip_id, int options); static void parse_eeprom(struct device *dev); static int read_eeprom(long ioaddr, int location); static int mdio_read(struct device *dev, int phy_id, int location); @@ -446,51 +460,34 @@ static void tulip_init_ring(struct device *dev); static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); static int tulip_rx(struct device *dev); -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); -static struct enet_statistics *tulip_get_stats(struct device *dev); +static struct net_device_stats *tulip_get_stats(struct device *dev); #ifdef HAVE_PRIVATE_IOCTL static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); #endif -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev); -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); -#endif -/* A list of all installed Tulip devices, for removing the driver module. */ +/* A list of all installed Tulip devices. */ static struct device *root_tulip_dev = NULL; -/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, - but now receives directly into full-sized skbuffs that are allocated - at open() time. - This allows the probe routine to use the old driver initialization - interface. */ - +#ifndef CARDBUS int tulip_probe(struct device *dev) { int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ + int pci_index = 0; unsigned char pci_bus, pci_device_fn; - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Tulip cards in slot order. */ - -#if LINUX_VERSION_CODE >= 0x20155 - if (! pci_present()) - return -ENODEV; -#else - if (! pcibios_present()) + if ( ! pcibios_present()) return -ENODEV; -#endif + for (;pci_index < 0xff; pci_index++) { u16 vendor, device, pci_command, new_command; - u32 pci_ioaddr; - int chip_idx = 0; + int chip_idx; + int irq; + long ioaddr; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, @@ -505,50 +502,55 @@ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) - if (vendor == tulip_tbl[chip_idx].vendor_id && - device == tulip_tbl[chip_idx].device_id) + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) break; - if (tulip_tbl[chip_idx].chip_name == 0) { - if (vendor == PCI_VENDOR_ID_DEC || - vendor == PCI_VENDOR_ID_LITEON) - printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" - " %4.4x %4.4x"" detected: not configured.\n", - vendor, device); + if (pci_tbl[chip_idx].vendor_id == 0) continue; - } -#if LINUX_VERSION_CODE >= 0x20155 - pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; +#else + u32 pci_ioaddr; + u8 pci_irq_line; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; #endif - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + } - if (tulip_debug > 2) - printk(KERN_DEBUG "Found %s at I/O %#x.\n", - tulip_tbl[chip_idx].chip_name, pci_ioaddr); + if (debug > 2) + printk(KERN_INFO "Found %s at PCI I/O address %#lx.\n", + pci_tbl[chip_idx].name, ioaddr); - if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) continue; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } - dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); /* Get and check the bus-master and latency values. */ if (dev) { - unsigned char pci_latency; + u8 pci_latency; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 10) { @@ -557,30 +559,22 @@ pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 64); - } else if (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " - " PCI command is %4.4x.\n", - pci_latency, new_command); - /* Bring the 21143 out power-down mode. */ - if (device == PCI_DEVICE_ID_DEC_TULIP_21142) - pcibios_write_config_dword(pci_bus, pci_device_fn, - 0x40, 0x40000000); - dev = 0; - cards_found++; + } } + dev = 0; + cards_found++; } return cards_found ? 0 : -ENODEV; } +#endif /* not CARDBUS */ -static struct device *tulip_probe1(int pci_bus, int pci_device_fn, - struct device *dev, - int chip_id, int board_idx) +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chip_idx, int board_idx) { static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; - long ioaddr; - int irq; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; @@ -593,36 +587,23 @@ dev = init_etherdev(dev, 0); -#if LINUX_VERSION_CODE >= 0x20155 - irq = pci_find_slot(pci_bus, pci_device_fn)->irq; - ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - { - u8 pci_irq_line; - u32 pci_ioaddr; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); - irq = pci_irq_line; - ioaddr = pci_ioaddr; - } -#endif - /* Remove I/O space marker in bit 0. */ - ioaddr &= ~3; + /* Bring the 21143 out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (tulip_tbl[chip_idx].flags & HAS_ACPI) + pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000); printk(KERN_INFO "%s: %s at %#3lx,", - dev->name, tulip_tbl[chip_id].chip_name, ioaddr); + dev->name, tulip_tbl[chip_idx].chip_name, ioaddr); /* Stop the chip's Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* Clear the missed-packet counter. */ (volatile int)inl(ioaddr + CSR8); - if (chip_id == DC21041) { + if (chip_idx == DC21041) { if (inl(ioaddr + CSR9) & 0x8000) { printk(" 21040 compatible mode,"); - chip_id = DC21040; + chip_idx = DC21040; } else { printk(" 21041 mode,"); } @@ -633,7 +614,7 @@ EEPROM. */ sum = 0; - if (chip_id == DC21040) { + if (chip_idx == DC21040) { outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ for (i = 0; i < 6; i++) { int value, boguscnt = 100000; @@ -643,7 +624,7 @@ dev->dev_addr[i] = value; sum += value & 0xff; } - } else if (chip_id == LC82C168) { + } else if (chip_idx == LC82C168) { for (i = 0; i < 3; i++) { int value, boguscnt = 100000; outl(0x600 | i, ioaddr + 0x98); @@ -705,8 +686,8 @@ last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ - /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ - request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); + /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ + request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; @@ -720,8 +701,8 @@ root_tulip_dev = dev; tp->pci_bus = pci_bus; - tp->pci_dev_fn = pci_device_fn; - tp->chip_id = chip_id; + tp->pci_devfn = pci_devfn; + tp->chip_id = chip_idx; #ifdef TULIP_FULL_DUPLEX tp->full_duplex = 1; @@ -753,7 +734,7 @@ tp->full_duplex_lock = 1; /* This is logically part of probe1(), but too complex to write inline. */ - if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) + if (tulip_tbl[chip_idx].flags & HAS_MEDIA_TABLE) parse_eeprom(dev); if (media_cap[tp->default_port] & MediaIsMII) { @@ -762,9 +743,13 @@ } else tp->to_advertise = 0x03e1; - if ((tp->mtable && tp->mtable->has_mii) || - ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { + if ((tulip_tbl[chip_idx].flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tulip_tbl[chip_idx].flags & HAS_MII))) { int phy, phy_idx; + /* Clear undefined bits that confuse the MII interface. */ + if (tp->chip_id == DC21142) + outl(inl(ioaddr+CSR15) & ~(0x800037c0), ioaddr+CSR15); /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes much time. */ @@ -773,16 +758,18 @@ int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { int mii_reg0 = mdio_read(dev, phy, 0); + int mii_advert = mdio_read(dev, phy, 4); int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; tp->phys[phy_idx] = phy; tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver found at MDIO address " - "%d, config %4.4x status %4.4x.\n", - dev->name, phy, mii_reg0, mii_status); - if (1 || (media_cap[tp->default_port] & MediaIsMII)) { + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != reg4) { printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," " previously advertising %4.4x.\n", - dev->name, reg4, phy, mdio_read(dev, phy, 4)); + dev->name, reg4, phy, mii_advert); mdio_write(dev, phy, 4, reg4); } /* Enable autonegotiation: some boards default to off. */ @@ -812,7 +799,7 @@ #endif /* Reset the xcvr interface and turn on heartbeat. */ - switch (chip_id) { + switch (chip_idx) { case DC21041: outl(0x00000000, ioaddr + CSR13); outl(0xFFFFFFFF, ioaddr + CSR14); @@ -829,12 +816,19 @@ outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); break; case DC21142: - outl(0x82420200, ioaddr + CSR6); - outl(0x0001, ioaddr + CSR13); - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + if (tp->mii_cnt) { + outl(0x82020000, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x820E0000, ioaddr + CSR6); + } else { + outl(0x82420200, ioaddr + CSR6); + outl(0x0001, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + } break; case LC82C168: if ( ! tp->mii_cnt) { @@ -882,7 +876,7 @@ 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ - 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ }}, {0, 0, 0, 0, {}}}; @@ -961,7 +955,7 @@ } printk("\n"); } - + controller_index = 0; if (ee_data[19] > 1) { /* Multiport board. */ last_ee_data = ee_data; @@ -1024,7 +1018,7 @@ media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { struct medialeaf *leaf = &mtable->mleaf[i]; - + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ leaf->type = 0; leaf->media = p[0] & 0x3f; @@ -1070,13 +1064,9 @@ #define EE_ENB (0x4800 | EE_CS) /* Delay between EEPROM clock transitions. - The 1.2 code is a "nasty" timing loop, but PC compatible machines are - *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ -#ifdef _LINUX_DELAY_H -#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) -#else -#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) -#endif + Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. + We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) @@ -1089,28 +1079,26 @@ unsigned short retval = 0; long ee_addr = ioaddr + CSR9; int read_cmd = location | EE_READ_CMD; - + outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); - + /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB | dataval, ee_addr); - eeprom_delay(100); + eeprom_delay(); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); + eeprom_delay(); } outl(EE_ENB, ee_addr); - + for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + eeprom_delay(); retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); outl(EE_ENB, ee_addr); - eeprom_delay(100); + eeprom_delay(); } /* Terminate the EEPROM access. */ @@ -1150,13 +1138,15 @@ long ioaddr = dev->base_addr; int i = 1000; outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); while (--i > 0) if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) return retval & 0xffff; return 0xffff; } - /* Establish sync by sending at least 32 logic ones. */ + /* Establish sync by sending at least 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1201,7 +1191,7 @@ return; } - /* Establish sync by sending 32 logic ones. */ + /* Establish sync by sending 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1232,7 +1222,7 @@ { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; - int i = 0; + int i; /* On some chip revs we must set the MII/SYM port before the reset!? */ if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) @@ -1240,11 +1230,11 @@ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ outl(0x00000001, ioaddr + CSR0); -#ifdef _LINUX_DELAY_H - udelay(2); -#else - SLOW_DOWN_IO; -#endif + + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + MOD_INC_USE_COUNT; + /* Deassert reset. 486: Set 8 longword cache alignment, 8 longword burst. 586: Set 16 longword cache alignment, no burst limit. @@ -1270,34 +1260,18 @@ #endif outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); if (x86 <= 4) - printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " - "alignment to %x.\n", dev->name, - 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); + printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " + "alignment to %x.\n", dev->name, + 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); #endif #else outl(0x01A00000 | 0x4800, ioaddr + CSR0); #warning Processor architecture undefined! #endif -#ifdef SA_SHIRQ - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { - return -EAGAIN; - } -#else - if (irq2dev_map[dev->irq] != NULL - || (irq2dev_map[dev->irq] = dev) == NULL - || dev->irq == 0 - || request_irq(dev->irq, &tulip_interrupt, 0, - tulip_tbl[tp->chip_id].chip_name)) { - return -EAGAIN; - } -#endif - if (tulip_debug > 1) printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); - MOD_INC_USE_COUNT; - tulip_init_ring(dev); /* This is set_rx_mode(), but without starting the transmitter. */ @@ -1327,6 +1301,7 @@ outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + tp->saved_if_port = dev->if_port; if (dev->if_port == 0) dev->if_port = tp->default_port; if (tp->chip_id == DC21041 && dev->if_port > 4) @@ -1334,6 +1309,7 @@ dev->if_port = 4; /* Allow selecting a default media. */ + i = 0; if (tp->mtable == NULL) goto media_picked; if (dev->if_port) { @@ -1362,15 +1338,36 @@ tp->csr6 = 0; tp->cur_index = i; if (dev->if_port == 0 && tp->chip_id == DC21142) { - tp->csr6 = 0x82420200; - outl(0x0003FFFF, ioaddr + CSR14); - outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); + if (tp->mii_cnt) { + if (tulip_debug > 1) + printk(KERN_INFO "%s: Using MII transceiver %d, status " + "%4.4x.\n", + dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); + select_media(dev, 1); + outl(0x82020000, ioaddr + CSR6); + tp->csr6 = 0x820E0000; + dev->if_port = 11; + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + } else { + if (tulip_debug > 1) + printk(KERN_INFO "%s: Using default 21143 media sense.\n", + dev->name); + tp->csr6 = 0x82420200; + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); + } } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { dev->if_port = 11; tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); outl(0x0001, ioaddr + CSR15); + } else if (tp->chip_id == MX98713 && ! tp->medialock) { + dev->if_port = 0; + tp->csr6 = 0x01a80000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); } else select_media(dev, 1); @@ -1435,7 +1432,7 @@ dev->if_port = p[0] & 15; if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control %4.4x/%4.4x.\n", dev->name, medianame[dev->if_port], setup[0], setup[1]); if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ outl(0, ioaddr + CSR13); @@ -1793,9 +1790,47 @@ int new_csr6 = 0; if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n", dev->name, csr12, medianame[dev->if_port]); - if (dev->if_port == 3) { + if (media_cap[dev->if_port] & MediaIsMII) { + int mii_reg1 = mdio_read(dev, tp->phys[0], 1); + int mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x, CSR6 %x.\n", + dev->name, mii_reg1, mii_reg5, inl(ioaddr + CSR6)); + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + printk(KERN_INFO "%s: No link beat on the MII interface," + " status then %4.4x now %4.4x.\n", + dev->name, mii_reg1, new_reg1); + } + if (tp->full_duplex_lock) + ; + else { + int negotiated = mii_reg5 & tp->advertising[0]; + int duplex = ((negotiated & 0x0100) != 0 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + else + tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (tulip_debug > 0) + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + " Xcvr #%d parter capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + } + } + next_tick = 60*HZ; + } + } else if (dev->if_port == 3) { if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ new_csr6 = 0x82420200; outl(new_csr6, ioaddr + CSR6); @@ -1808,7 +1843,7 @@ } else if ((csr12 & 0x7000) != 0x5000) { /* Negotiation failed. Search media types. */ if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n", + printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", dev->name, csr12); if (!(csr12 & 4)) { /* 10mbps link beat good. */ new_csr6 = 0x82420000; @@ -1834,7 +1869,7 @@ outl(1, ioaddr + CSR13); } if (tulip_debug > 1) - printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + printk(KERN_INFO"%s: Testing new 21143 media %s.\n", dev->name, medianame[dev->if_port]); if (new_csr6 != (tp->csr6 & ~0x00D5)) { tp->csr6 &= 0x00D5; @@ -1855,7 +1890,7 @@ int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n", + printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x.\n", dev->name, csr12, inl(ioaddr + CSR5)); if ((csr12 & 0x7000) == 0x5000) { @@ -1873,20 +1908,20 @@ } /* Else 10baseT-FD is handled automatically. */ } else if (dev->if_port == 3) { if (!(csr12 & 2)) - printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n", + printk(KERN_INFO"%s: 21143 100baseTx link beat good.\n", dev->name); else dev->if_port = 0; } else if (dev->if_port == 0) { if (!(csr12 & 4)) - printk(KERN_INFO"%s: 21142 10baseT link beat good.\n", + printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", dev->name); } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ - printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + printk(KERN_INFO"%s: 21143 10mpbs sensed media.\n", dev->name); dev->if_port = 0; } else { /* 100mbps link beat good. */ - printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n", + printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n", dev->name); dev->if_port = 3; tp->csr6 = 0x83860000; @@ -1896,7 +1931,6 @@ outl(tp->csr6 | 0x2002, ioaddr + CSR6); } } - static void mxic_timer(unsigned long data) { @@ -2002,96 +2036,88 @@ static void tulip_tx_timeout(struct device *dev) { - struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21040) { - if (inl(ioaddr + CSR12) & 0x0002) { - printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "10baseT" : "AUI"); - dev->if_port ^= 1; - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); - } - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21041) { - u32 csr12 = inl(ioaddr + CSR12); + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } else if (tp->chip_id == DC21040) { + if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) { + dev->if_port ^= 1; + printk(KERN_INFO "%s: transmit timed out, switching to " + "%s10baseT media.\n", + dev->name, dev->if_port ? "non" : ""); + outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + int csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " + "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if ( ! tp->medialock) { + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + } + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713) { + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if ( ! tp->medialock && tp->mtable) { + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s " + "media.\n", dev->name, medianame[dev->if_port]); + } + } else { + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " + "%8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); + dev->if_port = 0; + } + +#ifdef way_too_many_messages + printk(" Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); +#endif + + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); - printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," - " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), csr12, - inl(ioaddr + CSR13), inl(ioaddr + CSR14)); - tp->mediasense = 1; - if (dev->if_port == 1 || dev->if_port == 2) - if (csr12 & 0x0004) { - dev->if_port = 2 - dev->if_port; - } else - dev->if_port = 0; - else - dev->if_port = 1; - select_media(dev, 0); - tp->stats.tx_errors++; dev->trans_start = jiffies; + tp->stats.tx_errors++; return; - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713) { - /* Stop the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), - inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - if (tp->mtable) { - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - select_media(dev, 0); - printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - } - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - tp->stats.tx_errors++; - dev->trans_start = jiffies; - return; - } else - printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," - " resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); -#ifdef way_too_many_messages - printk(" Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); -#endif - - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - /* Stop and restart the chip's Tx processes . */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - tp->stats.tx_errors++; - return; } /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -tulip_init_ring(struct device *dev) +static void tulip_init_ring(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; @@ -2108,7 +2134,7 @@ dev_alloc_skb() provides 16 byte alignment. But do *not* use skb_reserve() to align the IP header! */ struct sk_buff *skb; - skb = DEV_ALLOC_SKB(PKT_BUF_SZ); + skb = dev_alloc_skb(PKT_BUF_SZ); tp->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ @@ -2122,7 +2148,7 @@ tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); } /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; + tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP; tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); /* The Tx buffer descriptor is filled in as needed, but we @@ -2175,7 +2201,7 @@ tp->tx_full = 1; } if (entry == TX_RING_SIZE-1) - flag |= 0xe2000000; + flag |= 0xe0000000 | DESC_RING_WRAP; tp->tx_ring[entry].length = skb->len | flag; tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ @@ -2190,39 +2216,28 @@ /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { -#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = (struct device *)dev_instance; -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif - - struct tulip_private *tp; - long ioaddr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; int csr5, work_budget = max_interrupt_work; - if (dev == NULL) { - printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", - irq); - return; - } - - ioaddr = dev->base_addr; - tp = (struct tulip_private *)dev->priv; - if (test_and_set_bit(0, (void*)&tp->interrupt)) { -#ifdef SMP_CHECK +#if defined(__i386__) && defined(SMP_CHECK) /* && defined(__SMP__) */ + if (test_and_set_bit(0, (void*)&dev->interrupt)) { printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d," " proc %d already handling.\n", dev->name, tp->smp_proc_id, hard_smp_processor_id()); + dev->interrupt = 0; + return; + } else + tp->smp_proc_id = hard_smp_processor_id(); #else + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); -#endif return; } dev->interrupt = 1; -#ifdef SMP_CHECK - tp->smp_proc_id = hard_smp_processor_id(); #endif do { @@ -2283,11 +2298,7 @@ } /* Free the original skb. */ -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[entry]); -#else - dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } @@ -2309,7 +2320,7 @@ tp->dirty_tx = dirty_tx; if (csr5 & TxDied) { - if (tulip_debug > 1) + if (tulip_debug) printk(KERN_WARNING "%s: The transmitter stopped!" " CSR5 is %x, CSR6 %x.\n", dev->name, csr5, inl(ioaddr + CSR6)); @@ -2320,6 +2331,8 @@ /* Log errors. */ if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; if (csr5 & TxJabber) tp->stats.tx_errors++; if (csr5 & TxFIFOUnderflow) { if ((tp->csr6 & 0xC000) != 0xC000) @@ -2342,7 +2355,7 @@ if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000) && tp->chip_id == DC21142) { if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", + printk(KERN_INFO"%s: 21143 link change, CSR5 = %8.8x.\n", dev->name, csr5); t21142_lnk_change(dev); } @@ -2351,7 +2364,7 @@ } if (--work_budget < 0) { if (tulip_debug > 1) - printk(KERN_WARNING "%s: Too much work at interrupt, " + printk(KERN_WARNING "%s: Too much work during an interrupt, " "csr5=0x%8.8x.\n", dev->name, csr5); /* Acknowledge all interrupt sources. */ outl(0x8001ffff, ioaddr + CSR5); @@ -2367,8 +2380,11 @@ printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; - clear_bit(0, (void*)&tp->interrupt); +#endif return; } @@ -2389,69 +2405,67 @@ if (--rx_work_limit < 0) break; - if ((status & 0x0300) != 0x0300) { - if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %8.8x!\n", + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & 0x8000) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", dev->name, status); - tp->stats.rx_length_errors++; + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; } - } else if (status & 0x8000) { - /* There was a fatal error. */ - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) tp->stats.rx_length_errors++; - if (status & 0x0004) tp->stats.rx_frame_errors++; - if (status & 0x0002) tp->stats.rx_crc_errors++; - if (status & 0x0001) tp->stats.rx_fifo_errors++; } else { /* Omit the four octet CRC from the length. */ - short pkt_len = ((status >> 16) & 0x7FF) - 4; + short pkt_len = ((status >> 16) & 0x7ff) - 4; struct sk_buff *skb; - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ +#ifndef final_version + if (pkt_len > 1518) { + printk("%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak - && (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) { + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if LINUX_VERSION_CODE < 0x10300 - memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len); -#elif LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#else -#warning Code untested +#if ! defined(__alpha__) eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len, 0); skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); #endif work_done++; } else { /* Pass up the skb already on the Rx ring. */ - skb = tp->rx_skbuff[entry]; + char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); tp->rx_skbuff[entry] = NULL; #ifndef final_version - { - void *temp = skb_put(skb, pkt_len); - if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) - printk(KERN_ERR "%s: Internal consistency error! The " - "skbuff addresses do not match in tulip_rx:" - " %p vs. %p / %p.\n", dev->name, - bus_to_virt(tp->rx_ring[entry].buffer1), - skb->head, temp); - } -#else - skb_put(skb, pkt_len); + if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal fault: The skbuff addresses " + "do not match in tulip_rx: %p vs. %p / %p.\n", + dev->name, bus_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); #endif } -#if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); -#else - skb->len = pkt_len; -#endif netif_rx(skb); dev->last_rx = jiffies; tp->stats.rx_packets++; @@ -2467,15 +2481,11 @@ entry = tp->dirty_rx % RX_RING_SIZE; if (tp->rx_skbuff[entry] == NULL) { struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ); + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ -#if LINUX_VERSION_CODE > 0x10300 tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); -#else - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data); -#endif work_done++; } tp->rx_ring[entry].status = 0x80000000; @@ -2506,16 +2516,14 @@ if (tp->chip_id == DC21040) outl(0x00000004, ioaddr + CSR13); - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; del_timer(&tp->timer); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif + + dev->if_port = tp->saved_if_port; /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -2528,20 +2536,12 @@ #if LINUX_VERSION_CODE < 0x20100 skb->free = 1; #endif -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(skb); -#else - dev_kfree_skb(skb, FREE_WRITE); -#endif + dev_free_skb(skb); } } for (i = 0; i < TX_RING_SIZE; i++) { if (tp->tx_skbuff[i]) -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[i]); -#else - dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } @@ -2551,8 +2551,7 @@ return 0; } -static struct enet_statistics * -tulip_get_stats(struct device *dev) +static struct net_device_stats *tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; @@ -2575,7 +2574,7 @@ switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - if (tp->mtable && tp->mtable->has_mii) + if (tp->mii_cnt) data[0] = phy; else if (tp->chip_id == DC21142) data[0] = 32; @@ -2595,11 +2594,11 @@ + (csr12&0x06 ? 0x04 : 0); break; case 4: { - int csr14 = inl(ioaddr + CSR14); - data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1; + data[3] = ((csr14>>9)&0x0380) + + ((inl(ioaddr + CSR6)>>3)&0x0040) +((csr14>>1)&0x20) + 1; break; } - case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + case 5: data[3] = csr12 >> 16; break; default: data[3] = 0; break; } } else { @@ -2613,6 +2612,8 @@ if (!suser()) return -EPERM; if (data[0] == 32) { /* 21142 pseudo-MII */ + if (data[1] == 5) + tp->advertising[tp->mii_cnt] = data[2]; } else { save_flags(flags); cli(); @@ -2654,11 +2655,7 @@ return crc; } -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev) -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) -#endif { long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; @@ -2682,38 +2679,38 @@ int i; if (dev->mc_count > 14) { /* Must use a multicast hash table. */ - u16 hash_table[32]; - memset(hash_table, 0, sizeof(hash_table)); - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, - hash_table); - /* Copy the hash table to the setup frame. - NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ - for (i = 0; i < 32; i++) - *setup_frm++ = hash_table[i]; - setup_frm += 7; - tx_flags = 0x08400000 | 192; - /* Too clever: i > 15 for fall-though. */ + u16 hash_table[32]; + memset(hash_table, 0, sizeof(hash_table)); + /* This should work on big-endian machines as well. */ + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, + hash_table); + /* Copy the hash table to the setup frame. + NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ + for (i = 0; i < 32; i++) + *setup_frm++ = hash_table[i]; + setup_frm += 7; + tx_flags = 0x08400000 | 192; + /* Too clever: i > 15 for fall-though. */ } else { - /* We have <= 15 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - /* Note that only the low shortword of setup_frame[] is valid! - This code may require tweaking for non-x86 architectures! */ - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - } - /* Fill the rest of the table with our physical address. - Once again, only the low shortword or setup_frame[] is valid! */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - tx_flags = 0x08000000 | 192; + /* We have <= 15 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + /* Note that only the low shortword of setup_frame[] is valid! + This code may require tweaking for non-x86 architectures! */ + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs++; + *setup_frm++ = *eaddrs++; + } + /* Fill the rest of the table with our physical address. + Once again, only the low shortword or setup_frame[] is valid! */ + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + *setup_frm++ = 0xffff; + tx_flags = 0x08000000 | 192; } eaddrs = (u16 *)dev->dev_addr; do { @@ -2727,7 +2724,7 @@ } else { unsigned long flags; unsigned int entry; - + save_flags(flags); cli(); entry = tp->cur_tx++ % TX_RING_SIZE; @@ -2735,7 +2732,7 @@ /* Avoid a chip errata by prefixing a dummy entry. */ tp->tx_skbuff[entry] = 0; tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0; tp->tx_ring[entry].buffer1 = 0; tp->tx_ring[entry].status = 0x80000000; entry = tp->cur_tx++ % TX_RING_SIZE; @@ -2744,7 +2741,7 @@ tp->tx_skbuff[entry] = 0; /* Put the setup frame on the Tx list. */ if (entry == TX_RING_SIZE-1) - tx_flags |= 0x02000000; /* Wrap ring. */ + tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ tp->tx_ring[entry].length = tx_flags; tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); tp->tx_ring[entry].status = 0x80000000; @@ -2766,18 +2763,18 @@ static dev_node_t *tulip_attach(dev_locator_t *loc) { + struct device *dev; u16 dev_id; u32 io; - u8 bus, devfn; - struct device *dev; + u8 bus, devfn, irq; if (loc->bus != LOC_PCI) return NULL; bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); - io &= ~3; - dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + dev = tulip_probe1(bus, devfn, NULL, io & ~3, irq, DC21142, -1); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); @@ -2789,6 +2786,37 @@ return NULL; } +static void tulip_suspend(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + tulip_close(*devp); + /* Put the chip into sleep mode. */ + pcibios_write_config_dword(tp->pci_bus,tp->pci_devfn, 0x40,0x80000000); + } +} + +static void tulip_resume(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0x0000); + tulip_open(*devp); + } +} + static void tulip_detach(dev_node_t *node) { struct device **devp, **next; @@ -2807,34 +2835,17 @@ } struct driver_operations tulip_ops = { - "tulip_cb", tulip_attach, NULL, NULL, tulip_detach + "tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach }; #endif /* Cardbus support */ #ifdef MODULE -#if LINUX_VERSION_CODE > 0x20118 -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); -MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(reverse_probe, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -#endif - -/* An additional parameter that may be passed in... */ -static int debug = -1; - -int -init_module(void) +int init_module(void) { - if (debug >= 0) - tulip_debug = debug; - #ifdef CARDBUS + reverse_probe = 0; /* Not used. */ register_driver(&tulip_ops); return 0; #else @@ -2842,8 +2853,7 @@ #endif } -void -cleanup_module(void) +void cleanup_module(void) { struct device *next_dev; @@ -2853,9 +2863,11 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { - next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; + struct tulip_private *tp = (struct tulip_private *)root_tulip_dev->priv; + next_dev = tp->next_module; unregister_netdev(root_tulip_dev); - release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); + release_region(root_tulip_dev->base_addr, + tulip_tbl[tp->chip_id].io_size); kfree(root_tulip_dev); root_tulip_dev = next_dev; } @@ -2867,6 +2879,7 @@ * Local variables: * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.5/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/pci/pci.c linux/drivers/pci/pci.c --- linux.vanilla/drivers/pci/pci.c Sun Dec 6 00:14:41 1998 +++ linux/drivers/pci/pci.c Fri Dec 11 20:21:07 1998 @@ -219,6 +219,9 @@ DEVICE( X, X_AGX016, "ITT AGX016"), DEVICE( PICOP, PICOP_PT86C52X, "PT86C52x Vesuvius"), DEVICE( PICOP, PICOP_PT80C524, "PT80C524 Nile"), + DEVICE( MYLEX, MYLEX_DAC960P_V2,"DAC960P V2"), + DEVICE( MYLEX, MYLEX_DAC960P_V3,"DAC960P V3"), + DEVICE( MYLEX, MYLEX_DAC960P_V4,"DAC960P V4"), DEVICE( APPLE, APPLE_BANDIT, "Bandit"), DEVICE( APPLE, APPLE_GC, "Grand Central"), DEVICE( APPLE, APPLE_HYDRA, "Hydra"), @@ -336,6 +339,7 @@ DEVICE( VIA, VIA_82C586_3, "VT 82C586B Apollo ACPI"), DEVICE( VIA, VIA_86C100A, "VT 86C100A"), DEVICE( VIA, VIA_82C597_1, "VT 82C597 Apollo VP3 AGP"), + DEVICE( SMC2, SMC2_1211TX, "1211 TX"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), DEVICE( VORTEX, VORTEX_GDT6x10, "GDT 6110/6510"), @@ -472,11 +476,13 @@ DEVICE( S3, S3_ViRGE_MXP, "ViRGE/MX+"), DEVICE( S3, S3_ViRGE_MXPMV, "ViRGE/MX+MV"), DEVICE( S3, S3_SONICVIBES, "SonicVibes"), + DEVICE( DCI, DCI_PCCOM4, "PC COM PCI Bus 4 port serial Adapter"), DEVICE( INTEL, INTEL_82375, "82375EB"), BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + BRIDGE( INTEL, INTEL_I960, "i960", 0x00), DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), @@ -662,8 +668,8 @@ case PCI_CLASS_STORAGE_SCSI: return "SCSI storage controller"; case PCI_CLASS_STORAGE_IDE: return "IDE interface"; case PCI_CLASS_STORAGE_FLOPPY: return "Floppy disk controller"; - case PCI_CLASS_STORAGE_IPI: return "IPI bus controller"; - case PCI_CLASS_STORAGE_RAID: return "RAID bus controller"; + case PCI_CLASS_STORAGE_IPI: return "IPI storage controller"; + case PCI_CLASS_STORAGE_RAID: return "RAID storage controller"; case PCI_CLASS_STORAGE_OTHER: return "Unknown mass storage controller"; case PCI_CLASS_NETWORK_ETHERNET: return "Ethernet controller"; @@ -769,6 +775,7 @@ case PCI_VENDOR_ID_N9: return "Number Nine"; case PCI_VENDOR_ID_UMC: return "UMC"; case PCI_VENDOR_ID_X: return "X TECHNOLOGY"; + case PCI_VENDOR_ID_MYLEX: return "Mylex"; case PCI_VENDOR_ID_NEXGEN: return "Nexgen"; case PCI_VENDOR_ID_QLOGIC: return "Q Logic"; case PCI_VENDOR_ID_LEADTEK: return "Leadtek Research"; @@ -798,6 +805,7 @@ case PCI_VENDOR_ID_TRUEVISION: return "Truevision"; case PCI_VENDOR_ID_INIT: return "Initio Corp"; case PCI_VENDOR_ID_VIA: return "VIA Technologies"; + case PCI_VENDOR_ID_SMC2: return "SMC"; case PCI_VENDOR_ID_VORTEX: return "VORTEX"; case PCI_VENDOR_ID_EF: return "Efficient Networks"; case PCI_VENDOR_ID_FORE: return "Fore Systems"; @@ -831,6 +839,7 @@ case PCI_VENDOR_ID_AVANCE: return "Avance"; case PCI_VENDOR_ID_NETVIN: return "NetVin"; case PCI_VENDOR_ID_S3: return "S3 Inc."; + case PCI_VENDOR_ID_DCI: return "Decision Computer Int."; case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_KTI: return "KTI"; case PCI_VENDOR_ID_ADAPTEC: return "Adaptec"; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- linux.vanilla/drivers/scsi/Config.in Sun Dec 6 00:14:41 1998 +++ linux/drivers/scsi/Config.in Fri Dec 11 19:10:07 1998 @@ -15,6 +15,7 @@ comment 'SCSI low-level drivers' dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI +dep_tristate 'ACARD SCSI support' CONFIG_SCSI_ACARD $CONFIG_SCSI dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI @@ -52,6 +53,7 @@ "Port CONFIG_SCSI_G_NCR5380_PORT \ Memory CONFIG_SCSI_G_NCR5380_MEM" Port fi +dep_tristate 'Initio 91XX support' CONFIG_SCSI_INITIO $CONFIG_SCSI dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI @@ -87,8 +89,11 @@ dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI fi dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI -if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_AM53C974" != "y" ]; then - dep_tristate 'Tekram DC-390(T) SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI + if [ "$CONFIG_SCSI_DC390T" != "n" ]; then + bool ' _omit_ support for non-DC390 adapters' CONFIG_SCSI_DC390T_NOGENSUPP + fi fi dep_tristate 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 $CONFIG_SCSI dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/DAC960.c linux/drivers/scsi/DAC960.c --- linux.vanilla/drivers/scsi/DAC960.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/DAC960.c Mon Nov 30 07:59:59 1998 @@ -0,0 +1,1979 @@ +/* + + Linux Driver for Mylex DAC960 PCI RAID Controllers + + Copyright 1998 by Leonard N. Zubkoff + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation. + + 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 complete details. + + The author respectfully requests that any modifications to this software be + sent directly to him for evaluation and testing. + +*/ + + +#define DAC960_DriverVersion "2.0.0 Beta3" +#define DAC960_DriverDate "29 November 1998" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DAC960.h" + + +/* + DAC960_ControllerCount is the number of DAC960 Controllers. +*/ + +static int + DAC960_ControllerCount = 0; + + +/* + DAC960_Controllers is an array of pointers to the DAC960 Controller + structures. +*/ + +static DAC960_Controller_T + *DAC960_Controllers[DAC960_MaxControllers] = { NULL }; + + +/* + DAC960_FileOperations is the File Operations structure for DAC960 Logical + Disk Devices. +*/ + +static FileOperations_T + DAC960_FileOperations = + { lseek: NULL, + read: block_read, + write: block_write, + readdir: NULL, + select: NULL, + ioctl: DAC960_Ioctl, + mmap: NULL, + open: DAC960_Open, + release: DAC960_Release, + fsync: block_fsync, + fasync: NULL, + check_media_change: NULL, + revalidate: NULL }; + + +/* + DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name, + Copyright Notice, and Electronic Mail Address. +*/ + +static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller) +{ + DAC960_Announce("***** DAC960 RAID Driver Version " + DAC960_DriverVersion " of " + DAC960_DriverDate " *****\n", Controller); + DAC960_Announce("Copyright 1998 by Leonard N. Zubkoff " + "\n", Controller); +} + + +/* + DAC960_Failure prints a standardized error message, and then returns false. +*/ + +static boolean DAC960_Failure(DAC960_Controller_T *Controller, + char *ErrorMessage) +{ + DAC960_Error("While configuring DAC960 PCI RAID Controller at\n", + Controller); + if (Controller->IO_Address == 0) + DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A " + "PCI Address 0x%X\n", Controller, + Controller->Bus, Controller->Device, + Controller->Function, Controller->PCI_Address); + else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address " + "0x%X PCI Address 0x%X\n", Controller, + Controller->Bus, Controller->Device, + Controller->Function, Controller->IO_Address, + Controller->PCI_Address); + DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage); + return false; +} + + +/* + DAC960_ClearCommand clears critical fields of Command. +*/ + +static inline void DAC960_ClearCommand(DAC960_Command_T *Command) +{ + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + CommandMailbox->Words[0] = 0; + CommandMailbox->Words[1] = 0; + CommandMailbox->Words[2] = 0; + CommandMailbox->Words[3] = 0; + Command->CommandStatus = 0; +} + + +/* + DAC960_AllocateCommand allocates a Command structure from Controller's + free list. +*/ + +static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T + *Controller) +{ + DAC960_Command_T *Command = Controller->FreeCommands; + if (Command == NULL) return NULL; + Controller->FreeCommands = Command->Next; + Command->Next = NULL; + return Command; +} + + +/* + DAC960_DeallocateCommand deallocates Command, returning it to Controller's + free list. +*/ + +static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + Command->Next = Controller->FreeCommands; + Controller->FreeCommands = Command; +} + + +/* + DAC960_QueueCommand queues Command. +*/ + +static void DAC960_QueueCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_V4_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + DAC960_V4_CommandMailbox_T *NextCommandMailbox; + CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands; + switch (Controller->ControllerType) + { + case DAC960_V4_Controller: + NextCommandMailbox = Controller->NextCommandMailbox; + DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox); + if (Controller->PreviousCommandMailbox->Words[0] == 0) + DAC960_V4_NewCommand(ControllerBaseAddress); + Controller->PreviousCommandMailbox = NextCommandMailbox; + if (++NextCommandMailbox > Controller->LastCommandMailbox) + NextCommandMailbox = Controller->FirstCommandMailbox; + Controller->NextCommandMailbox = NextCommandMailbox; + break; + case DAC960_V3_Controller: + while (DAC960_V3_MailboxFullP(ControllerBaseAddress)) + udelay(1); + DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox); + DAC960_V3_NewCommand(ControllerBaseAddress); + break; + } +} + + +/* + DAC960_ExecuteCommand executes Command and waits for completion. It + returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command) +{ + Semaphore_T Semaphore = MUTEX_LOCKED; + Command->Semaphore = &Semaphore; + DAC960_QueueCommand(Command); + down(&Semaphore); + return Command->CommandStatus == DAC960_NormalCompletion; +} + + +/* + DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for + completion. It returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller, + DAC960_CommandOpcode_T CommandOpcode, + void *DataPointer) +{ + DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + boolean Result; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3.CommandOpcode = CommandOpcode; + CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer); + Result = DAC960_ExecuteCommand(Command); + DAC960_DeallocateCommand(Command); + return Result; +} + + +/* + DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for + completion. It returns true on success and false on failure. +*/ + +static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller, + DAC960_CommandOpcode_T CommandOpcode, + unsigned char Channel, + unsigned char TargetID, + void *DataPointer) +{ + DAC960_Command_T *Command = DAC960_AllocateCommand(Controller); + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + boolean Result; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_ImmediateCommand; + CommandMailbox->Type3D.CommandOpcode = CommandOpcode; + CommandMailbox->Type3D.Channel = Channel; + CommandMailbox->Type3D.TargetID = TargetID; + CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer); + Result = DAC960_ExecuteCommand(Command); + DAC960_DeallocateCommand(Command); + return Result; +} + + +/* + DAC960_V4_EnableMemoryMailboxInterface enables the V4 Memory Mailbox + Interface. +*/ + +static boolean DAC960_V4_EnableMemoryMailboxInterface(DAC960_Controller_T + *Controller) +{ + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_V4_CommandMailbox_T *CommandMailboxesMemory; + DAC960_V4_StatusMailbox_T *StatusMailboxesMemory; + DAC960_CommandMailbox_T CommandMailbox; + DAC960_CommandStatus_T CommandStatus; + CommandMailboxesMemory = + (DAC960_V4_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1, 0); + memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1); + Controller->FirstCommandMailbox = CommandMailboxesMemory; + CommandMailboxesMemory += DAC960_CommandMailboxCount - 1; + Controller->LastCommandMailbox = CommandMailboxesMemory; + Controller->NextCommandMailbox = Controller->FirstCommandMailbox; + Controller->PreviousCommandMailbox = Controller->LastCommandMailbox; + StatusMailboxesMemory = + (DAC960_V4_StatusMailbox_T *) (CommandMailboxesMemory + 1); + Controller->FirstStatusMailbox = StatusMailboxesMemory; + StatusMailboxesMemory += DAC960_StatusMailboxCount - 1; + Controller->LastStatusMailbox = StatusMailboxesMemory; + Controller->NextStatusMailbox = Controller->FirstStatusMailbox; + /* Enable the Memory Mailbox Interface. */ + CommandMailbox.TypeX.CommandOpcode = 0x2B; + CommandMailbox.TypeX.CommandIdentifier = 0; + CommandMailbox.TypeX.CommandOpcode2 = 0x10; + CommandMailbox.TypeX.CommandMailboxesBusAddress = + Virtual_to_Bus(Controller->FirstCommandMailbox); + CommandMailbox.TypeX.StatusMailboxesBusAddress = + Virtual_to_Bus(Controller->FirstStatusMailbox); + while (DAC960_V4_MailboxFullP(ControllerBaseAddress)) + udelay(1); + DAC960_V4_WriteLegacyCommand(ControllerBaseAddress, &CommandMailbox); + DAC960_V4_NewCommand(ControllerBaseAddress); + while (!DAC960_V4_StatusAvailableP(ControllerBaseAddress)) + udelay(1); + CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress); + DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress); + DAC960_V4_AcknowledgeStatus(ControllerBaseAddress); + return CommandStatus == DAC960_NormalCompletion; +} + + +/* + DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating + the PCI Configuration Space for DeviceID. +*/ + +static void DAC960_DetectControllers(unsigned short DeviceID) +{ + unsigned char Bus, DeviceFunction, IRQ_Channel; + unsigned int BaseAddress0, BaseAddress1; + unsigned int MemoryWindowSize = 0; + unsigned short Index = 0; + while (pcibios_find_device(PCI_VENDOR_ID_MYLEX, DeviceID, + Index++, &Bus, &DeviceFunction) == 0) + { + DAC960_Controller_T *Controller = (DAC960_Controller_T *) + kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC); + DAC960_ControllerType_T ControllerType = 0; + DAC960_IO_Address_T IO_Address = 0; + DAC960_PCI_Address_T PCI_Address = 0; + unsigned char Device = DeviceFunction >> 3; + unsigned char Function = DeviceFunction & 0x7; + pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_0, &BaseAddress0); + pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_1, &BaseAddress1); + pcibios_read_config_byte(Bus, DeviceFunction, + PCI_INTERRUPT_LINE, &IRQ_Channel); + switch (DeviceID) + { + case PCI_DEVICE_ID_MYLEX_DAC960P_V4: + ControllerType = DAC960_V4_Controller; + PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK; + MemoryWindowSize = DAC960_V4_RegisterWindowSize; + break; + case PCI_DEVICE_ID_MYLEX_DAC960P_V3: + ControllerType = DAC960_V3_Controller; + IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; + PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK; + MemoryWindowSize = DAC960_V3_RegisterWindowSize; + break; + } + if (DAC960_ControllerCount == DAC960_MaxControllers) + { + DAC960_Error("More than %d DAC960 Controllers detected - " + "ignoring from Controller at\n", + NULL, DAC960_MaxControllers); + goto Failure; + } + if (Controller == NULL) + { + DAC960_Error("Unable to allocate Controller structure for " + "Controller at\n", NULL); + goto Failure; + } + memset(Controller, 0, sizeof(DAC960_Controller_T)); + Controller->ControllerNumber = DAC960_ControllerCount; + DAC960_Controllers[DAC960_ControllerCount++] = Controller; + if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS) + { + DAC960_Error("IRQ Channel %d illegal for Controller at\n", + NULL, IRQ_Channel); + goto Failure; + } + Controller->ControllerType = ControllerType; + Controller->IO_Address = IO_Address; + Controller->PCI_Address = PCI_Address; + Controller->Bus = Bus; + Controller->Device = Device; + Controller->Function = Function; + /* + Acquire shared access to the IRQ Channel. + */ + strcpy(Controller->FullModelName, "DAC960"); + if (request_irq(IRQ_Channel, DAC960_InterruptHandler, + SA_INTERRUPT | SA_SHIRQ, Controller->FullModelName, + Controller) < 0) + { + DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n", + NULL, IRQ_Channel); + goto Failure; + } + Controller->IRQ_Channel = IRQ_Channel; + /* + Map the Controller Register Window. + */ + if (MemoryWindowSize < PAGE_SIZE) + MemoryWindowSize = PAGE_SIZE; + Controller->MemoryMappedAddress = + ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize); + Controller->BaseAddress = + Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK); + if (Controller->MemoryMappedAddress == NULL) + { + DAC960_Error("Unable to map Controller Register Window for " + "Controller at\n", NULL); + goto Failure; + } + switch (DeviceID) + { + case PCI_DEVICE_ID_MYLEX_DAC960P_V4: + DAC960_V4_DisableInterrupts(Controller->BaseAddress); + if (!DAC960_V4_EnableMemoryMailboxInterface(Controller)) + { + DAC960_Error("Unable to Enable V4 Memory Mailbox Interface " + "for Controller at\n", NULL); + goto Failure; + } + DAC960_V4_EnableInterrupts(Controller->BaseAddress); + break; + case PCI_DEVICE_ID_MYLEX_DAC960P_V3: + request_region(Controller->IO_Address, 0x80, + Controller->FullModelName); + DAC960_V3_EnableInterrupts(Controller->BaseAddress); + break; + } + Controller->Commands[0].Controller = Controller; + Controller->Commands[0].Next = NULL; + Controller->FreeCommands = &Controller->Commands[0]; + continue; + Failure: + if (IO_Address == 0) + DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A " + "PCI Address 0x%X\n", NULL, + Bus, Device, Function, PCI_Address); + else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address " + "0x%X PCI Address 0x%X\n", NULL, + Bus, Device, Function, IO_Address, PCI_Address); + if (Controller == NULL) break; + if (Controller->IRQ_Channel > 0) + free_irq(IRQ_Channel, Controller); + if (Controller->MemoryMappedAddress != NULL) + iounmap(Controller->MemoryMappedAddress); + kfree(Controller); + break; + } +} + + +/* + DAC960_ReadControllerConfiguration reads the Configuration Information + from Controller and initializes the Controller structure. +*/ + +static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T + *Controller) +{ + DAC960_Enquiry2_T Enquiry2; + DAC960_Config2_T Config2; + int Channel, TargetID; + if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry, + &Controller->Enquiry[0])) + return DAC960_Failure(Controller, "ENQUIRY"); + if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2)) + return DAC960_Failure(Controller, "ENQUIRY2"); + if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2)) + return DAC960_Failure(Controller, "READ CONFIG2"); + if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation, + &Controller->LogicalDriveInformation[0])) + return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION"); + for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++) + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState, + Channel, TargetID, + &Controller->DeviceState[0][Channel][TargetID])) + return DAC960_Failure(Controller, "GET DEVICE STATE"); + /* + Initialize the Controller Model Name and Full Model Name fields. + */ + switch (Enquiry2.HardwareID.SubModel) + { + case DAC960_P_PD_PU: + if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra) + strcpy(Controller->ModelName, "DAC960PU"); + else strcpy(Controller->ModelName, "DAC960PD"); + break; + case DAC960_PL: + strcpy(Controller->ModelName, "DAC960PL"); + break; + case DAC960_PG: + strcpy(Controller->ModelName, "DAC960PG"); + break; + case DAC960_PJ: + strcpy(Controller->ModelName, "DAC960PJ"); + break; + case DAC960_PTL_0: + strcpy(Controller->ModelName, "DAC960PTL-0"); + break; + case DAC960_PTL_1: + strcpy(Controller->ModelName, "DAC960PTL-1"); + break; + default: + return DAC960_Failure(Controller, "MODEL VERIFICATION"); + } + strcpy(Controller->FullModelName, "Mylex "); + strcat(Controller->FullModelName, Controller->ModelName); + /* + Initialize the Controller Firmware Version field and verify that it + is a supported firmware version. The supported firmware versions are: + + DAC960PTL/PJ/PG 4.06 and above + DAC960PU/PD/PL 3.51 and above + */ + sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d", + Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion, + Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID); + if (!((Controller->FirmwareVersion[0] == '4' && + strcmp(Controller->FirmwareVersion, "4.06") >= 0) || + (Controller->FirmwareVersion[0] == '3' && + strcmp(Controller->FirmwareVersion, "3.51") >= 0))) + { + DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION"); + DAC960_Error("Firmware Version = '%s'\n", Controller, + Controller->FirmwareVersion); + return false; + } + /* + Initialize the Controller Channels, Memory Size, and SAF-TE Fault + Management Enabled fields. + */ + Controller->Channels = Enquiry2.ActualChannels; + Controller->MemorySize = Enquiry2.MemorySize >> 20; + Controller->SAFTE_FaultManagementEnabled = + Enquiry2.FaultManagementType == DAC960_SAFTE; + /* + Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive + Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments. + The Driver Queue Depth must be at most one less than the Controller Queue + Depth to allow for an automatic drive rebuild operation. + */ + Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands; + Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1; + Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives; + Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand; + Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries; + /* + Initialize the Stripe Size, Segment Size, and Geometry Translation. + */ + Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor + >> (10 - DAC960_BlockSizeBits); + Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor + >> (10 - DAC960_BlockSizeBits); + switch (Config2.DriveGeometry) + { + case DAC960_Geometry_128_32: + Controller->GeometryTranslationHeads = 128; + Controller->GeometryTranslationSectors = 32; + break; + case DAC960_Geometry_255_63: + Controller->GeometryTranslationHeads = 255; + Controller->GeometryTranslationSectors = 63; + break; + default: + return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY"); + } + return true; +} + + +/* + DAC960_ReportControllerConfiguration reports the configuration of + Controller. +*/ + +static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T + *Controller) +{ + int LogicalDriveNumber, Channel, TargetID; + DAC960_Info("Configuring Mylex %s PCI RAID Controller\n", + Controller, Controller->ModelName); + DAC960_Info(" Firmware Version: %s, Channels: %d, Memory Size: %dMB\n", + Controller, Controller->FirmwareVersion, + Controller->Channels, Controller->MemorySize); + DAC960_Info(" PCI Bus: %d, Device: %d, Function: %d, I/O Address: ", + Controller, Controller->Bus, + Controller->Device, Controller->Function); + if (Controller->IO_Address == 0) + DAC960_Info("Unassigned\n", Controller); + else DAC960_Info("0x%X\n", Controller, Controller->IO_Address); + DAC960_Info(" PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n", + Controller, Controller->PCI_Address, + (unsigned long) Controller->BaseAddress, + Controller->IRQ_Channel); + DAC960_Info(" Controller Queue Depth: %d, " + "Maximum Blocks per Command: %d\n", + Controller, Controller->ControllerQueueDepth, + Controller->MaxBlocksPerCommand); + DAC960_Info(" Driver Queue Depth: %d, " + "Maximum Scatter/Gather Segments: %d\n", + Controller, Controller->DriverQueueDepth, + Controller->MaxScatterGatherSegments); + DAC960_Info(" Stripe Size: %dKB, Segment Size: %dKB, " + "BIOS Geometry: %d/%d\n", Controller, + Controller->StripeSize, + Controller->SegmentSize, + Controller->GeometryTranslationHeads, + Controller->GeometryTranslationSectors); + if (Controller->SAFTE_FaultManagementEnabled) + DAC960_Info(" SAF-TE Fault Management Enabled\n", Controller); + DAC960_Info(" Physical Devices:\n", Controller); + for (Channel = 0; Channel < Controller->Channels; Channel++) + for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++) + { + DAC960_DeviceState_T *DeviceState = + &Controller->DeviceState[0][Channel][TargetID]; + if (!DeviceState->Present) continue; + switch (DeviceState->DeviceType) + { + case DAC960_OtherType: + DAC960_Info(" %d:%d - Other\n", Controller, Channel, TargetID); + break; + case DAC960_DiskType: + DAC960_Info(" %d:%d - Disk: %s, %d blocks\n", Controller, + Channel, TargetID, + (DeviceState->DeviceState == DAC960_Device_Dead + ? "Dead" + : DeviceState->DeviceState == DAC960_Device_WriteOnly + ? "Write-Only" + : DeviceState->DeviceState == DAC960_Device_Online + ? "Online" : "Standby"), + DeviceState->DiskSize); + break; + case DAC960_SequentialType: + DAC960_Info(" %d:%d - Sequential\n", Controller, + Channel, TargetID); + break; + case DAC960_CDROM_or_WORM_Type: + DAC960_Info(" %d:%d - CD-ROM or WORM\n", Controller, + Channel, TargetID); + break; + } + + } + DAC960_Info(" Logical Drives:\n", Controller); + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + { + DAC960_LogicalDriveInformation_T *LogicalDriveInformation = + &Controller->LogicalDriveInformation[0][LogicalDriveNumber]; + DAC960_Info(" /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n", + Controller, Controller->ControllerNumber, LogicalDriveNumber, + LogicalDriveInformation->RAIDLevel, + (LogicalDriveInformation->LogicalDriveState == + DAC960_LogicalDrive_Online + ? "Online" + : LogicalDriveInformation->LogicalDriveState == + DAC960_LogicalDrive_Critical + ? "Critical" : "Offline"), + LogicalDriveInformation->LogicalDriveSize, + (LogicalDriveInformation->WriteBack + ? "Write Back" : "Write Thru")); + } + DAC960_Info("\n", Controller); + return true; +} + + +/* + DAC960_RegisterBlockDevice registers the Block Device structures + associated with Controller. +*/ + +static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) +{ + static void (*RequestFunctions[DAC960_MaxControllers])(void) = + { DAC960_RequestFunction0, DAC960_RequestFunction1, + DAC960_RequestFunction2, DAC960_RequestFunction3, + DAC960_RequestFunction4, DAC960_RequestFunction5, + DAC960_RequestFunction6, DAC960_RequestFunction7 }; + int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + GenericDiskInfo_T *GenericDiskInfo; + int MinorNumber; + /* + Register the Block Device Major Number for this DAC960 Controller. + */ + if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0) + { + DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n", + Controller, MajorNumber); + return false; + } + /* + Initialize the I/O Request Function. + */ + blk_dev[MajorNumber].request_fn = + RequestFunctions[Controller->ControllerNumber]; + /* + Initialize the Disk Partitions array, Partition Sizes array, Block Sizes + array, Max Sectors per Request array, and Max Segments per Request array. + */ + for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++) + { + Controller->BlockSizes[MinorNumber] = BLOCK_SIZE; + Controller->MaxSectorsPerRequest[MinorNumber] = + Controller->MaxBlocksPerCommand; + Controller->MaxSegmentsPerRequest[MinorNumber] = + Controller->MaxScatterGatherSegments; + } + Controller->GenericDiskInfo.part = Controller->DiskPartitions; + Controller->GenericDiskInfo.sizes = Controller->PartitionSizes; + blksize_size[MajorNumber] = Controller->BlockSizes; + max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest; + max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest; + /* + Initialize Read Ahead to 128 sectors. + */ + read_ahead[MajorNumber] = 128; + /* + Complete initialization of the Generic Disk Information structure. + */ + Controller->GenericDiskInfo.major = MajorNumber; + Controller->GenericDiskInfo.major_name = "rd"; + Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits; + Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions; + Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives; + Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo; + Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount; + Controller->GenericDiskInfo.real_devices = Controller; + Controller->GenericDiskInfo.next = NULL; + /* + Install the Generic Disk Information structure at the end of the list. + */ + if ((GenericDiskInfo = gendisk_head) != NULL) + { + while (GenericDiskInfo->next != NULL) + GenericDiskInfo = GenericDiskInfo->next; + GenericDiskInfo->next = &Controller->GenericDiskInfo; + } + else gendisk_head = &Controller->GenericDiskInfo; + /* + Indicate the Block Device Registration completed successfully, + */ + return true; +} + + +/* + DAC960_UnregisterBlockDevice unregisters the Block Device structures + associated with Controller. +*/ + +static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller) +{ + int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + /* + Unregister the Block Device Major Number for this DAC960 Controller. + */ + unregister_blkdev(MajorNumber, "rd"); + /* + Remove the I/O Request Function. + */ + blk_dev[MajorNumber].request_fn = NULL; + /* + Remove the Disk Partitions array, Partition Sizes array, Block Sizes + array, Max Sectors per Request array, and Max Segments per Request array. + */ + Controller->GenericDiskInfo.part = NULL; + Controller->GenericDiskInfo.sizes = NULL; + blk_size[MajorNumber] = NULL; + blksize_size[MajorNumber] = NULL; + max_sectors[MajorNumber] = NULL; + max_segments[MajorNumber] = NULL; + /* + Remove the Generic Disk Information structure from the list. + */ + if (gendisk_head != &Controller->GenericDiskInfo) + { + GenericDiskInfo_T *GenericDiskInfo = gendisk_head; + while (GenericDiskInfo != NULL && + GenericDiskInfo->next != &Controller->GenericDiskInfo) + GenericDiskInfo = GenericDiskInfo->next; + if (GenericDiskInfo != NULL) + GenericDiskInfo->next = GenericDiskInfo->next->next; + } + else gendisk_head = Controller->GenericDiskInfo.next; +} + + +/* + DAC960_InitializeController initializes Controller. +*/ + +static void DAC960_InitializeController(DAC960_Controller_T *Controller) +{ + DAC960_AnnounceDriver(Controller); + if (DAC960_ReadControllerConfiguration(Controller) && + DAC960_ReportControllerConfiguration(Controller) && + DAC960_RegisterBlockDevice(Controller)) + { + /* + Initialize the Command structures. + */ + DAC960_Command_T *Commands = Controller->Commands; + int CommandIdentifier; + Controller->FreeCommands = NULL; + for (CommandIdentifier = 0; + CommandIdentifier < Controller->DriverQueueDepth; + CommandIdentifier++) + { + Commands[CommandIdentifier].Controller = Controller; + Commands[CommandIdentifier].Next = Controller->FreeCommands; + Controller->FreeCommands = &Commands[CommandIdentifier]; + } + /* + Initialize the Monitoring Timer. + */ + init_timer(&Controller->MonitoringTimer); + Controller->MonitoringTimer.expires = + jiffies + DAC960_MonitoringTimerInterval; + Controller->MonitoringTimer.data = (unsigned long) Controller; + Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction; + add_timer(&Controller->MonitoringTimer); + } + else + { + free_irq(Controller->IRQ_Channel, Controller); + iounmap(Controller->MemoryMappedAddress); + DAC960_UnregisterBlockDevice(Controller); + DAC960_Controllers[Controller->ControllerNumber] = NULL; + kfree(Controller); + } +} + + +/* + DAC960_Initialize initializes the DAC960 Driver. +*/ + +void DAC960_Initialize(void) +{ + int ControllerNumber; + DAC960_DetectControllers(PCI_DEVICE_ID_MYLEX_DAC960P_V4); + DAC960_DetectControllers(PCI_DEVICE_ID_MYLEX_DAC960P_V3); + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + if (DAC960_Controllers[ControllerNumber] != NULL) + DAC960_InitializeController(DAC960_Controllers[ControllerNumber]); +} + + +/* + DAC960_Finalize flushes all DAC960 caches before the system halts. +*/ + +void DAC960_Finalize(void) +{ + int ControllerNumber; + for (ControllerNumber = 0; + ControllerNumber < DAC960_ControllerCount; + ControllerNumber++) + { + DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL) continue; + DAC960_Notice("Flushing Cache...", Controller); + DAC960_ExecuteType3(Controller, DAC960_Flush, NULL); + DAC960_Notice("done\n", Controller); + } +} + + +/* + DAC960_ProcessRequest attempts to remove one I/O Request from Controller's + I/O Request Queue and queues it to the Controller. Command is either a + previously allocated Command to be reused, or NULL if a new Command is to + be allocated for this I/O Request. It returns true if an I/O Request was + queued and false otherwise. +*/ + +static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, + DAC960_Command_T *Command) +{ + int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + IO_Request_T *Request = blk_dev[MajorNumber].current_request; + DAC960_CommandMailbox_T *CommandMailbox; + char *RequestBuffer; + if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false; + if (Command == NULL) + Command = DAC960_AllocateCommand(Controller); + if (Command == NULL) return false; + DAC960_ClearCommand(Command); + if (Request->cmd == READ) + Command->CommandType = DAC960_ReadCommand; + else Command->CommandType = DAC960_WriteCommand; + Command->Semaphore = Request->sem; + Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev); + Command->BlockNumber = + Request->sector + + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect; + Command->BlockCount = Request->nr_sectors; + Command->SegmentCount = Request->nr_segments; + Command->BufferHeader = Request->bh; + RequestBuffer = Request->buffer; + Request->rq_status = RQ_INACTIVE; + blk_dev[MajorNumber].current_request = Request->next; + wake_up(&wait_for_request); + CommandMailbox = &Command->CommandMailbox; + if (Command->SegmentCount == 1) + { + if (Command->CommandType == DAC960_ReadCommand) + CommandMailbox->Type5.CommandOpcode = DAC960_Read; + else CommandMailbox->Type5.CommandOpcode = DAC960_Write; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer); + } + else + { + DAC960_ScatterGatherSegment_T + *ScatterGatherList = Command->ScatterGatherList; + BufferHeader_T *BufferHeader = Command->BufferHeader; + char *LastDataEndPointer = NULL; + int SegmentNumber = 0; + if (Command->CommandType == DAC960_ReadCommand) + CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather; + else + CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList); + CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount; + while (BufferHeader != NULL) + { + if (BufferHeader->b_data == LastDataEndPointer) + { + ScatterGatherList[SegmentNumber-1].SegmentByteCount += + BufferHeader->b_size; + LastDataEndPointer += BufferHeader->b_size; + } + else + { + ScatterGatherList[SegmentNumber].SegmentDataPointer = + Virtual_to_Bus(BufferHeader->b_data); + ScatterGatherList[SegmentNumber].SegmentByteCount = + BufferHeader->b_size; + LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size; + if (SegmentNumber++ > Controller->MaxScatterGatherSegments) + panic("DAC960: Scatter/Gather Segment Overflow\n"); + } + BufferHeader = BufferHeader->b_reqnext; + } + if (SegmentNumber != Command->SegmentCount) + panic("DAC960: SegmentNumber != SegmentCount\n"); + } + DAC960_QueueCommand(Command); + return true; +} + + +/* + DAC960_ProcessRequests attempts to remove as many I/O Requests as possible + from Controller's I/O Request Queue and queue them to the Controller. +*/ + +static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller) +{ + while (Controller->FreeCommands != NULL) + if (!DAC960_ProcessRequest(Controller, NULL)) break; +} + + +/* + DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0. +*/ + +static void DAC960_RequestFunction0(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[0]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1. +*/ + +static void DAC960_RequestFunction1(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[1]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2. +*/ + +static void DAC960_RequestFunction2(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[2]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3. +*/ + +static void DAC960_RequestFunction3(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[3]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4. +*/ + +static void DAC960_RequestFunction4(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[4]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5. +*/ + +static void DAC960_RequestFunction5(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[5]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6. +*/ + +static void DAC960_RequestFunction6(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[6]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7. +*/ + +static void DAC960_RequestFunction7(void) +{ + DAC960_Controller_T *Controller = DAC960_Controllers[7]; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags); + /* + Process I/O Requests for Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags); +} + + +/* + DAC960_ReadWriteError prints an appropriate error message for Command when + an error occurs on a read or write operation. +*/ + +static void DAC960_ReadWriteError(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + char *CommandName = "UNKNOWN"; + switch (Command->CommandType) + { + case DAC960_ReadCommand: + case DAC960_ReadRetryCommand: + CommandName = "READ"; + break; + case DAC960_WriteCommand: + case DAC960_WriteRetryCommand: + CommandName = "WRITE"; + break; + case DAC960_MonitoringCommand: + case DAC960_ImmediateCommand: + break; + } + switch (Command->CommandStatus) + { + case DAC960_IrrecoverableDataError: + DAC960_Error("Irrecoverable Data Error on %s:\n", + Controller, CommandName); + break; + case DAC960_LogicalDriveNonexistentOrOffline: + break; + case DAC960_AccessBeyondEndOfLogicalDrive: + DAC960_Error("Attempt to Access Beyond End of Logical Drive " + "on %s:\n", Controller, CommandName); + break; + case DAC960_BadDataEncountered: + DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName); + break; + default: + DAC960_Error("Unexpected Error Status %04X on %s:\n", + Controller, Command->CommandStatus, CommandName); + break; + } + DAC960_Error(" /dev/rd/c%dd%d: absolute blocks %d..%d\n", + Controller, Controller->ControllerNumber, + Command->LogicalDriveNumber, Command->BlockNumber, + Command->BlockNumber + Command->BlockCount - 1); + if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0) + DAC960_Error(" /dev/rd/c%dd%dp%d: relative blocks %d..%d\n", + Controller, Controller->ControllerNumber, + Command->LogicalDriveNumber, + DAC960_PartitionNumber(Command->BufferHeader->b_rdev), + Command->BufferHeader->b_rsector, + Command->BufferHeader->b_rsector + Command->BlockCount - 1); +} + + +/* + DAC960_ProcessCompletedBuffer performs completion processing for an + individual Buffer. +*/ + +static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader, + boolean SuccessfulIO) +{ + mark_buffer_uptodate(BufferHeader, SuccessfulIO); + unlock_buffer(BufferHeader); +} + + +/* + DAC960_ProcessCompletedCommand performs completion processing for Command. +*/ + +static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + DAC960_CommandType_T CommandType = Command->CommandType; + DAC960_CommandStatus_T CommandStatus = Command->CommandStatus; + BufferHeader_T *BufferHeader = Command->BufferHeader; + if (CommandType == DAC960_ReadCommand || + CommandType == DAC960_WriteCommand) + { + if (CommandStatus == DAC960_NormalCompletion) + { + /* + Perform completion processing for all buffers in this I/O Request. + */ + while (BufferHeader != NULL) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + DAC960_ProcessCompletedBuffer(BufferHeader, true); + BufferHeader = NextBufferHeader; + } + /* + Wake up requestor for swap file paging requests. + */ + if (Command->Semaphore != NULL) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + } + } + else if ((CommandStatus == DAC960_IrrecoverableDataError || + CommandStatus == DAC960_BadDataEncountered) && + BufferHeader != NULL && + BufferHeader->b_reqnext != NULL) + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + if (CommandType == DAC960_ReadCommand) + { + Command->CommandType = DAC960_ReadRetryCommand; + CommandMailbox->Type5.CommandOpcode = DAC960_Read; + } + else + { + Command->CommandType = DAC960_WriteRetryCommand; + CommandMailbox->Type5.CommandOpcode = DAC960_Write; + } + Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.BusAddress = + Virtual_to_Bus(BufferHeader->b_data); + DAC960_QueueCommand(Command); + return; + } + else + { + DAC960_ReadWriteError(Command); + /* + Perform completion processing for all buffers in this I/O Request. + */ + while (BufferHeader != NULL) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + DAC960_ProcessCompletedBuffer(BufferHeader, false); + BufferHeader = NextBufferHeader; + } + /* + Wake up requestor for swap file paging requests. + */ + if (Command->Semaphore != NULL) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + } + } + } + else if (CommandType == DAC960_ReadRetryCommand || + CommandType == DAC960_WriteRetryCommand) + { + BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext; + BufferHeader->b_reqnext = NULL; + /* + Perform completion processing for this single buffer. + */ + if (CommandStatus == DAC960_NormalCompletion) + DAC960_ProcessCompletedBuffer(BufferHeader, true); + else + { + DAC960_ReadWriteError(Command); + DAC960_ProcessCompletedBuffer(BufferHeader, false); + } + if (NextBufferHeader != NULL) + { + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + Command->BlockNumber += + BufferHeader->b_size >> DAC960_BlockSizeBits; + Command->BlockCount = + NextBufferHeader->b_size >> DAC960_BlockSizeBits; + Command->BufferHeader = NextBufferHeader; + CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; + CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; + CommandMailbox->Type5.BusAddress = + Virtual_to_Bus(NextBufferHeader->b_data); + DAC960_QueueCommand(Command); + return; + } + } + else if (CommandType == DAC960_MonitoringCommand) + { + DAC960_CommandOpcode_T CommandOpcode = + Command->CommandMailbox.Common.CommandOpcode; + unsigned int OldCriticalLogicalDriveCount = 0; + unsigned int NewCriticalLogicalDriveCount = 0; + if (CommandOpcode == DAC960_Enquiry) + { + DAC960_Enquiry_T *OldEnquiry = + &Controller->Enquiry[Controller->EnquiryIndex]; + DAC960_Enquiry_T *NewEnquiry = + &Controller->Enquiry[Controller->EnquiryIndex ^= 1]; + OldCriticalLogicalDriveCount = OldEnquiry->CriticalLogicalDriveCount; + NewCriticalLogicalDriveCount = NewEnquiry->CriticalLogicalDriveCount; + if (NewEnquiry->StatusFlags.DeferredWriteError != + OldEnquiry->StatusFlags.DeferredWriteError) + DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller, + (NewEnquiry->StatusFlags.DeferredWriteError + ? "TRUE" : "FALSE")); + if ((NewCriticalLogicalDriveCount > 0 || + NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) || + (NewEnquiry->OfflineLogicalDriveCount > 0 || + NewEnquiry->OfflineLogicalDriveCount != + OldEnquiry->OfflineLogicalDriveCount) || + (NewEnquiry->DeadDriveCount > 0 || + NewEnquiry->DeadDriveCount != + OldEnquiry->DeadDriveCount) || + (NewEnquiry->EventLogSequenceNumber != + OldEnquiry->EventLogSequenceNumber) || + (jiffies - Controller->SecondaryMonitoringTime + >= DAC960_SecondaryMonitoringInterval)) + { + Controller->NeedLogicalDriveInformation = true; + Controller->NewEventLogSequenceNumber = + NewEnquiry->EventLogSequenceNumber; + Controller->NeedDeviceStateInformation = true; + Controller->DeviceStateChannel = 0; + Controller->DeviceStateTargetID = 0; + Controller->SecondaryMonitoringTime = jiffies; + } + if ((NewEnquiry->RebuildCount > 0 && + jiffies - Controller->RebuildLastReportTime + >= DAC960_RebuildStatusReportingInterval) || + NewEnquiry->RebuildCount != OldEnquiry->RebuildCount) + Controller->NeedRebuildProgress = true; + } + else if (CommandOpcode == DAC960_GetLogicalDriveInformation) + { + int LogicalDriveNumber; + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + { + DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation = + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber]; + DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation = + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1] + [LogicalDriveNumber]; + if (NewLogicalDriveInformation->LogicalDriveState != + OldLogicalDriveInformation->LogicalDriveState) + DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) " + "is now %s\n", Controller, + LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (NewLogicalDriveInformation->LogicalDriveState + == DAC960_LogicalDrive_Online + ? "ONLINE" + : NewLogicalDriveInformation->LogicalDriveState + == DAC960_LogicalDrive_Critical + ? "CRITICAL" : "OFFLINE")); + if (NewLogicalDriveInformation->WriteBack != + OldLogicalDriveInformation->WriteBack) + DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) " + "is now %s\n", Controller, + LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (NewLogicalDriveInformation->WriteBack + ? "WRITE BACK" : "WRITE THRU")); + } + Controller->LogicalDriveInformationIndex ^= 1; + } + else if (CommandOpcode == DAC960_PerformEventLogOperation) + { + DAC960_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry; + if (EventLogEntry->SequenceNumber == + Controller->OldEventLogSequenceNumber) + { + unsigned char SenseKey = EventLogEntry->SenseKey; + unsigned char AdditionalSenseCode = + EventLogEntry->AdditionalSenseCode; + unsigned char AdditionalSenseCodeQualifier = + EventLogEntry->AdditionalSenseCodeQualifier; + if (SenseKey == 9 && + AdditionalSenseCode == 0x80 && + AdditionalSenseCodeQualifier < DAC960_EventMessagesCount) + DAC960_Critical("Physical Drive %d:%d %s\n", Controller, + EventLogEntry->Channel, + EventLogEntry->TargetID, + DAC960_EventMessages[ + AdditionalSenseCodeQualifier]); + else if (!((SenseKey == 2 && + AdditionalSenseCode == 0x04 && + (AdditionalSenseCodeQualifier == 0x01 || + AdditionalSenseCodeQualifier == 0x02)) || + (SenseKey == 6 && AdditionalSenseCode == 0x29))) + DAC960_Critical("Physical Drive %d:%d Error Log: " + "Sense Key = %d, ASC = %02X, ASCQ = %02X\n", + Controller, + EventLogEntry->Channel, + EventLogEntry->TargetID, + SenseKey, + AdditionalSenseCode, + AdditionalSenseCodeQualifier); + } + Controller->OldEventLogSequenceNumber++; + } + else if (CommandOpcode == DAC960_GetDeviceState) + { + DAC960_DeviceState_T *OldDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + DAC960_DeviceState_T *NewDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex ^ 1] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + if (NewDeviceState->DeviceState != OldDeviceState->DeviceState) + DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller, + Controller->DeviceStateChannel, + Controller->DeviceStateTargetID, + (NewDeviceState->DeviceState == DAC960_Device_Dead + ? "DEAD" + : NewDeviceState->DeviceState + == DAC960_Device_WriteOnly + ? "WRITE-ONLY" + : NewDeviceState->DeviceState + == DAC960_Device_Online + ? "ONLINE" : "STANDBY")); + if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + { + Controller->DeviceStateChannel++; + Controller->DeviceStateTargetID = 0; + } + } + else if (CommandOpcode == DAC960_GetRebuildProgress) + { + unsigned int LogicalDriveNumber = + Controller->RebuildProgress.LogicalDriveNumber; + unsigned int LogicalDriveSize = + Controller->RebuildProgress.LogicalDriveSize; + unsigned int BlocksCompleted = + LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks; + switch (CommandStatus) + { + case DAC960_NormalCompletion: + DAC960_Critical("REBUILD IN PROGRESS: " + "Logical Drive %d (/dev/rd/c%dd%d) " + "%d%% completed\n", + Controller, LogicalDriveNumber, + Controller->ControllerNumber, + LogicalDriveNumber, + (100 * (BlocksCompleted >> 7)) + / (LogicalDriveSize >> 7)); + break; + case DAC960_RebuildFailed_LogicalDriveFailure: + DAC960_Critical("REBUILD FAILED due to " + "LOGICAL DRIVE FAILURE\n", Controller); + break; + case DAC960_RebuildFailed_BadBlocksOnOther: + DAC960_Critical("REBUILD FAILED due to " + "BAD BLOCKS ON OTHER DRIVES\n", Controller); + break; + case DAC960_RebuildFailed_NewDriveFailed: + DAC960_Critical("REBUILD FAILED due to " + "FAILURE OF DRIVE BEING REBUILT\n", Controller); + break; + case DAC960_RebuildSuccessful: + DAC960_Critical("REBUILD COMPLETED SUCCESSFULLY\n", Controller); + break; + case DAC960_NoRebuildOrCheckInProgress: + break; + } + Controller->RebuildLastReportTime = jiffies; + } + if (Controller->NeedLogicalDriveInformation && + NewCriticalLogicalDriveCount >= OldCriticalLogicalDriveCount) + { + Controller->NeedLogicalDriveInformation = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetLogicalDriveInformation; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus( + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1]); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NewEventLogSequenceNumber + - Controller->OldEventLogSequenceNumber > 0) + { + Command->CommandMailbox.Type3E.CommandOpcode = + DAC960_PerformEventLogOperation; + Command->CommandMailbox.Type3E.OperationType = + DAC960_GetEventLogEntry; + Command->CommandMailbox.Type3E.OperationQualifier = 1; + Command->CommandMailbox.Type3E.SequenceNumber = + Controller->OldEventLogSequenceNumber; + Command->CommandMailbox.Type3E.BusAddress = + Virtual_to_Bus(&Controller->EventLogEntry); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedDeviceStateInformation) + { + while (Controller->DeviceStateChannel < Controller->Channels) + { + DAC960_DeviceState_T *OldDeviceState = + &Controller->DeviceState[Controller->DeviceStateIndex] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]; + if (OldDeviceState->Present && + OldDeviceState->DeviceType == DAC960_DiskType) + { + Command->CommandMailbox.Type3D.CommandOpcode = + DAC960_GetDeviceState; + Command->CommandMailbox.Type3D.Channel = + Controller->DeviceStateChannel; + Command->CommandMailbox.Type3D.TargetID = + Controller->DeviceStateTargetID; + Command->CommandMailbox.Type3D.BusAddress = + Virtual_to_Bus(&Controller->DeviceState + [Controller->DeviceStateIndex ^ 1] + [Controller->DeviceStateChannel] + [Controller->DeviceStateTargetID]); + DAC960_QueueCommand(Command); + return; + } + if (++Controller->DeviceStateTargetID == DAC960_MaxTargets) + { + Controller->DeviceStateChannel++; + Controller->DeviceStateTargetID = 0; + } + } + Controller->NeedDeviceStateInformation = false; + Controller->DeviceStateIndex ^= 1; + } + if (Controller->NeedRebuildProgress) + { + Controller->NeedRebuildProgress = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetRebuildProgress; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus(&Controller->RebuildProgress); + DAC960_QueueCommand(Command); + return; + } + if (Controller->NeedLogicalDriveInformation && + NewCriticalLogicalDriveCount < OldCriticalLogicalDriveCount) + { + Controller->NeedLogicalDriveInformation = false; + Command->CommandMailbox.Type3.CommandOpcode = + DAC960_GetLogicalDriveInformation; + Command->CommandMailbox.Type3.BusAddress = + Virtual_to_Bus( + &Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex ^ 1]); + DAC960_QueueCommand(Command); + return; + } + Controller->MonitoringTimer.expires = + jiffies + DAC960_MonitoringTimerInterval; + add_timer(&Controller->MonitoringTimer); + } + else if (CommandType == DAC960_ImmediateCommand) + { + up(Command->Semaphore); + Command->Semaphore = NULL; + return; + } + else panic("DAC960: Unknown Command Type %d\n", CommandType); + /* + Queue a Monitoring Command to the Controller using the just completed + Command if one was deferred previously due to lack of a free Command when + the Monitoring Timer Function was called. + */ + if (Controller->MonitoringCommandDeferred) + { + Controller->MonitoringCommandDeferred = false; + DAC960_QueueMonitoringCommand(Command); + return; + } + /* + Attempt to remove a new I/O Request from the Controller's I/O Request + Queue and queue it to the Controller using the just completed Command. + If there is no I/O Request to be queued, deallocate the Command. + */ + if (!DAC960_ProcessRequest(Controller, Command)) + DAC960_DeallocateCommand(Command); +} + + +/* + DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers. +*/ + +static void DAC960_InterruptHandler(int IRQ_Channel, + void *DeviceIdentifier, + Registers_T *InterruptRegisters) +{ + DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier; + void *ControllerBaseAddress = Controller->BaseAddress; + DAC960_V4_StatusMailbox_T *NextStatusMailbox; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags); + /* + Process Hardware Interrupts for Controller. + */ + switch (Controller->ControllerType) + { + case DAC960_V4_Controller: + DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress); + NextStatusMailbox = Controller->NextStatusMailbox; + while (NextStatusMailbox->Fields.Valid) + { + DAC960_CommandIdentifier_T CommandIdentifier = + NextStatusMailbox->Fields.CommandIdentifier; + DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier]; + Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus; + NextStatusMailbox->Word = 0; + if (++NextStatusMailbox > Controller->LastStatusMailbox) + NextStatusMailbox = Controller->FirstStatusMailbox; + DAC960_ProcessCompletedCommand(Command); + } + Controller->NextStatusMailbox = NextStatusMailbox; + break; + case DAC960_V3_Controller: + while (DAC960_V3_StatusAvailableP(ControllerBaseAddress)) + { + DAC960_CommandIdentifier_T CommandIdentifier = + DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress); + DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier]; + Command->CommandStatus = + DAC960_V3_ReadStatusRegister(ControllerBaseAddress); + DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress); + DAC960_V3_AcknowledgeStatus(ControllerBaseAddress); + DAC960_ProcessCompletedCommand(Command); + } + break; + } + /* + Attempt to remove additional I/O Requests from the Controller's + I/O Request Queue and queue them to the Controller. + */ + DAC960_ProcessRequests(Controller); + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags); +} + + +/* + DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller. +*/ + +static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command) +{ + DAC960_Controller_T *Controller = Command->Controller; + DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; + DAC960_ClearCommand(Command); + Command->CommandType = DAC960_MonitoringCommand; + CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry; + CommandMailbox->Type3.BusAddress = + Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]); + DAC960_QueueCommand(Command); +} + + +/* + DAC960_MonitoringTimerFunction is the timer function for monitoring + the status of DAC960 Controllers. +*/ + +static void DAC960_MonitoringTimerFunction(unsigned long TimerData) +{ + DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData; + DAC960_Command_T *Command; + ProcessorFlags_T ProcessorFlags; + /* + Acquire exclusive access to Controller. + */ + DAC960_AcquireControllerLock(Controller, &ProcessorFlags); + /* + Queue a Status Monitoring Command for Controller; + */ + Command = DAC960_AllocateCommand(Controller); + if (Command != NULL) + DAC960_QueueMonitoringCommand(Command); + else Controller->MonitoringCommandDeferred = true; + /* + Release exclusive access to Controller. + */ + DAC960_ReleaseControllerLock(Controller, &ProcessorFlags); +} + + +/* + DAC960_Open is the Device Open Function for the DAC960 Driver. +*/ + +static int DAC960_Open(Inode_T *Inode, File_T *File) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + DAC960_Controller_T *Controller; + if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) + return -ENXIO; + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL || + LogicalDriveNumber > Controller->LogicalDriveCount - 1) + return -ENXIO; + if (Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber] .LogicalDriveState + == DAC960_LogicalDrive_Offline) + return -ENXIO; + if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0) + return -ENXIO; + /* + Increment Controller and Logical Drive Usage Counts. + */ + Controller->ControllerUsageCount++; + Controller->LogicalDriveUsageCount[LogicalDriveNumber]++; + return 0; +} + + +/* + DAC960_Release is the Device Release Function for the DAC960 Driver. +*/ + +static void DAC960_Release(Inode_T *Inode, File_T *File) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; + /* + Force any buffered data to be written. + */ + fsync_dev(Inode->i_rdev); + /* + Decrement the Logical Drive and Controller Usage Counts. + */ + Controller->LogicalDriveUsageCount[LogicalDriveNumber]--; + Controller->ControllerUsageCount--; +} + + +/* + DAC960_Ioctl is the Device Ioctl Function for the DAC960 Driver. +*/ + +static int DAC960_Ioctl(Inode_T *Inode, File_T *File, + unsigned int Request, unsigned long Argument) +{ + int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev); + int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev); + int PartitionNumber, ErrorCode; + unsigned short Cylinders; + DiskGeometry_T *Geometry; + DAC960_Controller_T *Controller; + if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) + return -ENXIO; + Controller = DAC960_Controllers[ControllerNumber]; + if (Controller == NULL || + LogicalDriveNumber > Controller->LogicalDriveCount - 1) + return -ENXIO; + switch (Request) + { + case HDIO_GETGEO: + /* Get BIOS Disk Geometry. */ + Geometry = (DiskGeometry_T *) Argument; + if (Geometry == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, Geometry, sizeof(DiskGeometry_T)); + if (ErrorCode != 0) return ErrorCode; + Cylinders = + Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex] + [LogicalDriveNumber].LogicalDriveSize + / (Controller->GeometryTranslationHeads * + Controller->GeometryTranslationSectors); + put_user(Controller->GeometryTranslationHeads, &Geometry->heads); + put_user(Controller->GeometryTranslationSectors, &Geometry->sectors); + put_user(Cylinders, &Geometry->cylinders); + put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)] + .start_sect, &Geometry->start); + return 0; + case BLKGETSIZE: + /* Get Device Size. */ + if ((long *) Argument == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, (long *) Argument, sizeof(long)); + if (ErrorCode != 0) return ErrorCode; + put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].nr_sects, + (long *) Argument); + return 0; + case BLKRAGET: + /* Get Read-Ahead. */ + if ((int *) Argument == NULL) return -EINVAL; + ErrorCode = verify_area(VERIFY_WRITE, (int *) Argument, sizeof(int)); + if (ErrorCode != 0) return ErrorCode; + put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument); + return 0; + case BLKRASET: + /* Set Read-Ahead. */ + if (!suser()) return -EACCES; + if (Argument > 256) return -EINVAL; + read_ahead[MAJOR(Inode->i_rdev)] = Argument; + return 0; + case BLKFLSBUF: + /* Flush Buffers. */ + if (!suser()) return -EACCES; + fsync_dev(Inode->i_rdev); + invalidate_buffers(Inode->i_rdev); + return 0; + case BLKRRPART: + /* Re-Read Partition Table. */ + if (!suser()) return -EACCES; + if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1) + return -EBUSY; + for (PartitionNumber = 0; + PartitionNumber < DAC960_MaxPartitions; + PartitionNumber++) + { + KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber, + LogicalDriveNumber, + PartitionNumber); + int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber, + PartitionNumber); + if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0) + continue; + /* + Flush all changes and invalidate buffered state. + */ + sync_dev(Device); + invalidate_inodes(Device); + invalidate_buffers(Device); + /* + Clear existing partition sizes. + */ + if (PartitionNumber > 0) + { + Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0; + Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0; + } + /* + Reset the Block Size so that the partition table can be read. + */ + set_blocksize(Device, BLOCK_SIZE); + } + resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber); + return 0; + } + return -EINVAL; +} + + +/* + DAC960_GenericDiskInit is the Generic Disk Information Initialization + Function for the DAC960 Driver. +*/ + +static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo) +{ + DAC960_Controller_T *Controller = + (DAC960_Controller_T *) GenericDiskInfo->real_devices; + DAC960_LogicalDriveInformation_T *LogicalDriveInformation = + Controller->LogicalDriveInformation + [Controller->LogicalDriveInformationIndex]; + int LogicalDriveNumber; + for (LogicalDriveNumber = 0; + LogicalDriveNumber < Controller->LogicalDriveCount; + LogicalDriveNumber++) + GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects = + LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize; +} + + +/* + DAC960_Message prints Driver Messages. +*/ + +static void DAC960_Message(DAC960_MessageLevel_T MessageLevel, + char *Format, + DAC960_Controller_T *Controller, + ...) +{ + static char Buffer[DAC960_LineBufferSize]; + static boolean BeginningOfLine = true; + va_list Arguments; + int Length = 0; + va_start(Arguments, Controller); + Length = vsprintf(Buffer, Format, Arguments); + va_end(Arguments); + if (MessageLevel == DAC960_AnnounceLevel) + { + static int AnnouncementLines = 0; + strcpy(&Controller->MessageBuffer[Controller->MessageBufferLength], + Buffer); + Controller->MessageBufferLength += Length; + if (++AnnouncementLines <= 2) + printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel], Buffer); + } + else if (MessageLevel == DAC960_InfoLevel) + { + strcpy(&Controller->MessageBuffer[Controller->MessageBufferLength], + Buffer); + Controller->MessageBufferLength += Length; + if (BeginningOfLine) + { + if (Buffer[0] != '\n' || Length > 1) + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + } + else printk("%s", Buffer); + } + else + { + if (BeginningOfLine) + printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel], + Controller->ControllerNumber, Buffer); + else printk("%s", Buffer); + } + BeginningOfLine = (Buffer[Length-1] == '\n'); +} diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/DAC960.h linux/drivers/scsi/DAC960.h --- linux.vanilla/drivers/scsi/DAC960.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/DAC960.h Mon Nov 30 07:59:59 1998 @@ -0,0 +1,1565 @@ +/* + + Linux Driver for Mylex DAC960 PCI RAID Controllers + + Copyright 1998 by Leonard N. Zubkoff + + This program is free software; you may redistribute and/or modify it under + the terms of the GNU General Public License Version 2 as published by the + Free Software Foundation. + + 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 complete details. + + The author respectfully requests that any modifications to this software be + sent directly to him for evaluation and testing. + +*/ + + +/* + DAC960_DriverVersion protects the private portion of this file. +*/ + +#ifdef DAC960_DriverVersion + + +/* + Define the maximum number of DAC960 Controllers supported by this driver. +*/ + +#define DAC960_MaxControllers 8 + + +/* + Define the maximum number of Controller Channels supported by this driver. +*/ + +#define DAC960_MaxChannels 3 + + +/* + Define the maximum number of Targets per Channel supported by this driver. +*/ + +#define DAC960_MaxTargets 16 + + +/* + Define the maximum number of Logical Drives supported by any DAC960 model. +*/ + +#define DAC960_MaxLogicalDrives 32 + + +/* + Define the maximum number of Partitions allowed for each Logical Drive. +*/ + +#define DAC960_MaxPartitions 8 +#define DAC960_MaxPartitionsBits 3 + + +/* + Define the maximum Driver Queue Depth and Controller Queue Depth supported + by any DAC960 model. +*/ + +#define DAC960_MaxDriverQueueDepth 127 +#define DAC960_MaxControllerQueueDepth 128 + + +/* + Define the maximum number of Scatter/Gather Segments supported by any + DAC960 model. +*/ + +#define DAC960_MaxScatterGatherSegments 33 + + +/* + Define the DAC960 Controller Monitoring Timer Interval. +*/ + +#define DAC960_MonitoringTimerInterval (7 * HZ) + + +/* + Define the DAC960 Controller Secondary Monitoring Interval. +*/ + +#define DAC960_SecondaryMonitoringInterval (60 * HZ) + + +/* + Define the DAC960 Controller Rebuild Status Reporting Interval. +*/ + +#define DAC960_RebuildStatusReportingInterval (60 * HZ) + + +/* + Define the number of Command Mailboxes and Status Mailboxes used by the + V4 Memory Mailbox Interface. +*/ + +#define DAC960_CommandMailboxCount 256 +#define DAC960_StatusMailboxCount 1024 + + +/* + Define macros to extract the Controller Number, Logical Drive Number, and + Partition Number from a Kernel Device, and to construct a Major Number, Minor + Number, and Kernel Device from the Controller Number, Logical Drive Number, + and Partition Number. There is one Major Number assigned to each Controller. + The associated Minor Number is divided into the Logical Drive Number and + Partition Number. +*/ + +#define DAC960_ControllerNumber(Device) \ + (MAJOR(Device) - DAC960_MAJOR) + +#define DAC960_LogicalDriveNumber(Device) \ + (MINOR(Device) >> DAC960_MaxPartitionsBits) + +#define DAC960_PartitionNumber(Device) \ + (MINOR(Device) & (DAC960_MaxPartitions - 1)) + +#define DAC960_MajorNumber(ControllerNumber) \ + (DAC960_MAJOR + (ControllerNumber)) + +#define DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber) \ + (((LogicalDriveNumber) << DAC960_MaxPartitionsBits) | (PartitionNumber)) + +#define DAC960_MinorCount (DAC960_MaxLogicalDrives \ + * DAC960_MaxPartitions) + +#define DAC960_KernelDevice(ControllerNumber, \ + LogicalDriveNumber, \ + PartitionNumber) \ + MKDEV(DAC960_MajorNumber(ControllerNumber), \ + DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber)) + + +/* + Define the DAC960 Controller fixed Block Size and Block Size Bits. +*/ + +#define DAC960_BlockSize 512 +#define DAC960_BlockSizeBits 9 + + +/* + Define the Controller Line and Message Buffer Sizes. +*/ + +#define DAC960_LineBufferSize 100 +#define DAC960_MessageBufferSize 2048 + + +/* + Define the Driver Message Levels. +*/ + +typedef enum DAC960_MessageLevel +{ + DAC960_AnnounceLevel = 0, + DAC960_InfoLevel = 1, + DAC960_NoticeLevel = 2, + DAC960_WarningLevel = 3, + DAC960_ErrorLevel = 4, + DAC960_CriticalLevel = 5 +} +DAC960_MessageLevel_T; + +static char + *DAC960_MessageLevelMap[] = + { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, + KERN_WARNING, KERN_ERR, KERN_CRIT }; + + +/* + Define Driver Message macros. +*/ + +#define DAC960_Announce(Format, Arguments...) \ + DAC960_Message(DAC960_AnnounceLevel, Format, ##Arguments) + +#define DAC960_Info(Format, Arguments...) \ + DAC960_Message(DAC960_InfoLevel, Format, ##Arguments) + +#define DAC960_Notice(Format, Arguments...) \ + DAC960_Message(DAC960_NoticeLevel, Format, ##Arguments) + +#define DAC960_Warning(Format, Arguments...) \ + DAC960_Message(DAC960_WarningLevel, Format, ##Arguments) + +#define DAC960_Error(Format, Arguments...) \ + DAC960_Message(DAC960_ErrorLevel, Format, ##Arguments) + +#define DAC960_Critical(Format, Arguments...) \ + DAC960_Message(DAC960_CriticalLevel, Format, ##Arguments) + + +/* + Define the types of DAC960 Controllers that are supported. +*/ + +typedef enum +{ + DAC960_V4_Controller = 1, /* DAC960PTL/PJ/PG */ + DAC960_V3_Controller = 2 /* DAC960PU/PD/PL */ +} +DAC960_ControllerType_T; + + +/* + Define a Boolean data type. +*/ + +typedef enum { false, true } __attribute__ ((packed)) boolean; + + +/* + Define a 32 bit I/O Address data type. +*/ + +typedef unsigned int DAC960_IO_Address_T; + + +/* + Define a 32 bit PCI Bus Address data type. +*/ + +typedef unsigned int DAC960_PCI_Address_T; + + +/* + Define a 32 bit Bus Address data type. +*/ + +typedef unsigned int DAC960_BusAddress_T; + + +/* + Define a 32 bit Byte Count data type. +*/ + +typedef unsigned int DAC960_ByteCount_T; + + +/* + Define types for some of the structures that interface with the rest + of the Linux Kernel and I/O Subsystem. +*/ + +typedef struct buffer_head BufferHeader_T; +typedef struct file File_T; +typedef struct file_operations FileOperations_T; +typedef struct gendisk GenericDiskInfo_T; +typedef struct hd_geometry DiskGeometry_T; +typedef struct hd_struct DiskPartition_T; +typedef struct inode Inode_T; +typedef kdev_t KernelDevice_T; +typedef unsigned long ProcessorFlags_T; +typedef struct pt_regs Registers_T; +typedef struct request IO_Request_T; +typedef struct semaphore Semaphore_T; +typedef struct timer_list Timer_T; + + +/* + Define the DAC960 V4 Controller Interface Register Offsets. +*/ + +#define DAC960_V4_RegisterWindowSize 0x2000 + +typedef enum +{ + DAC960_V4_InboundDoorBellRegisterOffset = 0x0020, + DAC960_V4_OutboundDoorBellRegisterOffset = 0x002C, + DAC960_V4_InterruptMaskRegisterOffset = 0x0034, + DAC960_V4_CommandOpcodeRegisterOffset = 0x1000, + DAC960_V4_CommandIdentifierRegisterOffset = 0x1001, + DAC960_V4_MailboxRegister2Offset = 0x1002, + DAC960_V4_MailboxRegister3Offset = 0x1003, + DAC960_V4_MailboxRegister4Offset = 0x1004, + DAC960_V4_MailboxRegister5Offset = 0x1005, + DAC960_V4_MailboxRegister6Offset = 0x1006, + DAC960_V4_MailboxRegister7Offset = 0x1007, + DAC960_V4_MailboxRegister8Offset = 0x1008, + DAC960_V4_MailboxRegister9Offset = 0x1009, + DAC960_V4_MailboxRegister10Offset = 0x100A, + DAC960_V4_MailboxRegister11Offset = 0x100B, + DAC960_V4_MailboxRegister12Offset = 0x100C, + DAC960_V4_StatusCommandIdentifierRegOffset = 0x1018, + DAC960_V4_StatusRegisterOffset = 0x101A +} +DAC960_V4_RegisterOffsets_T; + + +/* + Define the structure of the DAC960 V4 Inbound Door Bell Register. +*/ + +typedef union DAC960_V4_InboundDoorBellRegister +{ + unsigned int All; + struct { + boolean NewCommand:1; /* Bit 0 */ + boolean AcknowledgeStatus:1; /* Bit 1 */ + boolean SoftReset:1; /* Bit 2 */ + unsigned int :29; /* Bits 3-31 */ + } Write; + struct { + boolean MailboxFull:1; /* Bit 0 */ + unsigned int :31; /* Bits 1-31 */ + } Read; +} +DAC960_V4_InboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V4 Outbound Door Bell Register. +*/ + +typedef union DAC960_V4_OutboundDoorBellRegister +{ + unsigned int All; + struct { + boolean AcknowledgeInterrupt:1; /* Bit 0 */ + unsigned int :31; /* Bits 1-31 */ + } Write; + struct { + boolean StatusAvailable:1; /* Bit 0 */ + unsigned int :31; /* Bits 1-31 */ + } Read; +} +DAC960_V4_OutboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V4 Interrupt Mask Register. +*/ + +typedef union DAC960_V4_InterruptMaskRegister +{ + unsigned int All; + struct { + unsigned int MessageUnitInterruptMask1:2; /* Bits 0-1 */ + boolean DisableInterrupts:1; /* Bit 2 */ + unsigned int MessageUnitInterruptMask2:5; /* Bits 3-7 */ + unsigned int Reserved0:24; /* Bits 8-31 */ + } Bits; +} +DAC960_V4_InterruptMaskRegister_T; + + +/* + Define the DAC960 V3 Controller Interface Register Offsets. +*/ + +#define DAC960_V3_RegisterWindowSize 0x80 + +typedef enum +{ + DAC960_V3_CommandOpcodeRegisterOffset = 0x00, + DAC960_V3_CommandIdentifierRegisterOffset = 0x01, + DAC960_V3_MailboxRegister2Offset = 0x02, + DAC960_V3_MailboxRegister3Offset = 0x03, + DAC960_V3_MailboxRegister4Offset = 0x04, + DAC960_V3_MailboxRegister5Offset = 0x05, + DAC960_V3_MailboxRegister6Offset = 0x06, + DAC960_V3_MailboxRegister7Offset = 0x07, + DAC960_V3_MailboxRegister8Offset = 0x08, + DAC960_V3_MailboxRegister9Offset = 0x09, + DAC960_V3_MailboxRegister10Offset = 0x0A, + DAC960_V3_MailboxRegister11Offset = 0x0B, + DAC960_V3_MailboxRegister12Offset = 0x0C, + DAC960_V3_StatusCommandIdentifierRegOffset = 0x0D, + DAC960_V3_StatusRegisterOffset = 0x0E, + DAC960_V3_InboundDoorBellRegisterOffset = 0x40, + DAC960_V3_OutboundDoorBellRegisterOffset = 0x41, + DAC960_V3_InterruptEnableRegisterOffset = 0x43 +} +DAC960_V3_RegisterOffsets_T; + + +/* + Define the structure of the DAC960 V3 Inbound Door Bell Register. +*/ + +typedef union DAC960_V3_InboundDoorBellRegister +{ + unsigned char All; + struct { + boolean NewCommand:1; /* Bit 0 */ + boolean AcknowledgeStatus:1; /* Bit 1 */ + unsigned char :1; /* Bit 2 */ + boolean SoftReset:1; /* Bit 3 */ + unsigned char :4; /* Bits 4-7 */ + } Write; + struct { + boolean MailboxFull:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Read; +} +DAC960_V3_InboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V3 Outbound Door Bell Register. +*/ + +typedef union DAC960_V3_OutboundDoorBellRegister +{ + unsigned char All; + struct { + boolean AcknowledgeInterrupt:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Write; + struct { + boolean StatusAvailable:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Read; +} +DAC960_V3_OutboundDoorBellRegister_T; + + +/* + Define the structure of the DAC960 V3 Interrupt Enable Register. +*/ + +typedef union DAC960_V3_InterruptEnableRegister +{ + unsigned char All; + struct { + boolean EnableInterrupts:1; /* Bit 0 */ + unsigned char :7; /* Bits 1-7 */ + } Bits; +} +DAC960_V3_InterruptEnableRegister_T; + + +/* + Define the DAC960 Command Identifier type. +*/ + +typedef unsigned char DAC960_CommandIdentifier_T; + + +/* + Define the DAC960 Command Opcodes. +*/ + +typedef enum +{ + /* I/O Commands */ + DAC960_ReadExtended = 0x33, + DAC960_WriteExtended = 0x34, + DAC960_ReadAheadExtended = 0x35, + DAC960_ReadExtendedWithScatterGather = 0xB3, + DAC960_WriteExtendedWithScatterGather = 0xB4, + DAC960_Read = 0x36, + DAC960_ReadWithOldScatterGather = 0xB6, + DAC960_Write = 0x37, + DAC960_WriteWithOldScatterGather = 0xB7, + DAC960_DCDB = 0x04, + DAC960_DCDBWithScatterGather = 0x84, + DAC960_Flush = 0x0A, + /* Controller Status Related Commands */ + DAC960_Enquiry = 0x53, + DAC960_Enquiry2 = 0x1C, + DAC960_GetLogicalDriveElement = 0x55, + DAC960_GetLogicalDriveInformation = 0x19, + DAC960_IOPortRead = 0x39, + DAC960_IOPortWrite = 0x3A, + DAC960_GetSDStats = 0x3E, + DAC960_GetPDStats = 0x3F, + DAC960_PerformEventLogOperation = 0x72, + /* Device Related Commands */ + DAC960_StartDevice = 0x10, + DAC960_GetDeviceState = 0x50, + DAC960_StopChannel = 0x13, + DAC960_StartChannel = 0x12, + DAC960_ResetChannel = 0x1A, + /* Commands Associated with Data Consistency and Errors */ + DAC960_Rebuild = 0x09, + DAC960_RebuildAsync = 0x16, + DAC960_CheckConsistency = 0x0F, + DAC960_CheckConsistencyAsync = 0x1E, + DAC960_RebuildStat = 0x0C, + DAC960_GetRebuildProgress = 0x27, + DAC960_RebuildControl = 0x1F, + DAC960_ReadBadBlockTable = 0x0B, + DAC960_ReadBadDataTable = 0x25, + DAC960_ClearBadDataTable = 0x26, + DAC960_GetErrorTable = 0x17, + DAC960_AddCapacityAsync = 0x2A, + /* Configuration Related Commands */ + DAC960_ReadConfig2 = 0x3D, + DAC960_WriteConfig2 = 0x3C, + DAC960_ReadConfigurationOnDisk = 0x4A, + DAC960_WriteConfigurationOnDisk = 0x4B, + DAC960_ReadConfiguration = 0x4E, + DAC960_ReadBackupConfiguration = 0x4D, + DAC960_WriteConfiguration = 0x4F, + DAC960_AddConfiguration = 0x4C, + DAC960_ReadConfigurationLabel = 0x48, + DAC960_WriteConfigurationLabel = 0x49, + /* Firmware Upgrade Related Commands */ + DAC960_LoadImage = 0x20, + DAC960_StoreImage = 0x21, + DAC960_ProgramImage = 0x22, + /* Diagnostic Commands */ + DAC960_SetDiagnosticMode = 0x31, + DAC960_RunDiagnostic = 0x32, + /* Subsystem Service Commands */ + DAC960_GetSubsystemData = 0x70, + DAC960_SetSubsystemParameters = 0x71 +} +__attribute__ ((packed)) +DAC960_CommandOpcode_T; + + +/* + Define the DAC960 Command Status Codes. +*/ + +#define DAC960_NormalCompletion 0x0000 /* Common */ +#define DAC960_CheckConditionReceived 0x0002 /* Common */ +#define DAC960_NoDeviceAtAddress 0x0102 /* Common */ +#define DAC960_InvalidDeviceAddress 0x0105 /* Common */ +#define DAC960_InvalidParameter 0x0105 /* Common */ +#define DAC960_IrrecoverableDataError 0x0001 /* I/O */ +#define DAC960_LogicalDriveNonexistentOrOffline 0x0002 /* I/O */ +#define DAC960_AccessBeyondEndOfLogicalDrive 0x0105 /* I/O */ +#define DAC960_BadDataEncountered 0x010C /* I/O */ +#define DAC960_DeviceBusy 0x0008 /* DCDB */ +#define DAC960_DeviceNonresponsive 0x000E /* DCDB */ +#define DAC960_CommandTerminatedAbnormally 0x000F /* DCDB */ +#define DAC960_UnableToStartDevice 0x0002 /* Device */ +#define DAC960_InvalidChannelOrTarget 0x0105 /* Device */ +#define DAC960_ChannelBusy 0x0106 /* Device */ +#define DAC960_ChannelNotStopped 0x0002 /* Device */ +#define DAC960_AttemptToRebuildOnlineDrive 0x0002 /* Consistency */ +#define DAC960_RebuildBadBlocksEncountered 0x0003 /* Consistency */ +#define DAC960_NewDiskFailedDuringRebuild 0x0004 /* Consistency */ +#define DAC960_RebuildOrCheckAlreadyInProgress 0x0106 /* Consistency */ +#define DAC960_DependentDiskIsDead 0x0002 /* Consistency */ +#define DAC960_InconsistentBlocksFound 0x0003 /* Consistency */ +#define DAC960_InvalidOrNonredundantLogicalDrive 0x0105 /* Consistency */ +#define DAC960_NoRebuildOrCheckInProgress 0x0105 /* Consistency */ +#define DAC960_RebuildInProgress_DataValid 0x0000 /* Consistency */ +#define DAC960_RebuildFailed_LogicalDriveFailure 0x0002 /* Consistency */ +#define DAC960_RebuildFailed_BadBlocksOnOther 0x0003 /* Consistency */ +#define DAC960_RebuildFailed_NewDriveFailed 0x0004 /* Consistency */ +#define DAC960_RebuildSuccessful 0x0100 /* Consistency */ +#define DAC960_AddCapacityInProgress 0x0004 /* Consistency */ +#define DAC960_AddCapacityFailedOrSuspended 0x00F4 /* Consistency */ +#define DAC960_Config2ChecksumError 0x0002 /* Configuration */ +#define DAC960_ConfigurationSuspended 0x0106 /* Configuration */ +#define DAC960_FailedToConfigureNVRAM 0x0105 /* Configuration */ +#define DAC960_ConfigurationNotSavedStateChange 0x0106 /* Configuration */ +#define DAC960_SubsystemNotInstalled 0x0001 /* Subsystem */ +#define DAC960_SubsystemFailed 0x0002 /* Subsystem */ +#define DAC960_SubsystemBusy 0x0106 /* Subsystem */ + +typedef unsigned short DAC960_CommandStatus_T; + + +/* + Define the Enquiry reply structure. +*/ + +typedef struct DAC960_Enquiry +{ + unsigned char NumberOfLogicalDrives; /* Byte 0 */ + unsigned int :24; /* Bytes 1-3 */ + unsigned int LogicalDriveSizes[32]; /* Bytes 4-131 */ + unsigned short FlashAge; /* Bytes 132-133 */ + struct { + boolean DeferredWriteError:1; /* Byte 134 Bit 0 */ + boolean BatteryLow:1; /* Byte 134 Bit 1 */ + unsigned char :6; /* Byte 134 Bits 2-7 */ + } StatusFlags; + unsigned char :8; /* Byte 135 */ + unsigned char MinorFirmwareVersion; /* Byte 136 */ + unsigned char MajorFirmwareVersion; /* Byte 137 */ + enum { + DAC960_NoStandbyRebuildOrCheckInProgress = 0x00, + DAC960_StandbyRebuildInProgress = 0x01, + DAC960_BackgroundRebuildInProgress = 0x02, + DAC960_BackgroundCheckInProgress = 0x03, + DAC960_StandbyRebuildCOmpletedWithError = 0xFF, + DAC960_BackgroundRebuildOrCheckFailed_DriveFailed = 0xF0, + DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed = 0xF1, + DAC960_BackgroundRebuildOrCheckFailed_OtherCauses = 0xF2, + DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated = 0xF3 + } __attribute__ ((packed)) RebuildFlag; /* Byte 138 */ + unsigned char MaxCommands; /* Byte 139 */ + unsigned char OfflineLogicalDriveCount; /* Byte 140 */ + unsigned char :8; /* Byte 141 */ + unsigned short EventLogSequenceNumber; /* Bytes 142-143 */ + unsigned char CriticalLogicalDriveCount; /* Byte 144 */ + unsigned int :24; /* Bytes 145-147 */ + unsigned char DeadDriveCount; /* Byte 148 */ + unsigned char :8; /* Byte 149 */ + unsigned char RebuildCount; /* Byte 150 */ + struct { + unsigned char :3; /* Byte 151 Bits 0-2 */ + boolean BatteryBackupUnitPresent:1; /* Byte 151 Bit 3 */ + unsigned char :3; /* Byte 151 Bits 4-6 */ + unsigned char :1; /* Byte 151 Bit 7 */ + } MiscFlags; + struct { + unsigned char TargetID; + unsigned char Channel; + } DeadDrives[21]; /* Bytes 152-194 */ + unsigned char Reserved[62]; /* Bytes 195-255 */ +} +__attribute__ ((packed)) +DAC960_Enquiry_T; + + +/* + Define the Enquiry2 reply structure. +*/ + +typedef struct DAC960_Enquiry2 +{ + struct { + enum { + DAC960_P_PD_PU = 0x01, + DAC960_PL = 0x02, + DAC960_PG = 0x10, + DAC960_PJ = 0x11, + DAC960_PTL_0 = 0x14, + DAC960_PTL_1 = 0x16 + } __attribute__ ((packed)) SubModel; /* Byte 0 */ + unsigned char ActualChannels; /* Byte 1 */ + enum { + DAC960_FiveChannelBoard = 0x01, + DAC960_ThreeChannelBoard = 0x02, + DAC960_TwoChannelBoard = 0x03, + DAC960_ThreeChannelASIC_DAC = 0x04 + } __attribute__ ((packed)) Model; /* Byte 2 */ + enum { + DAC960_EISA_Controller = 0x01, + DAC960_MicroChannel_Controller = 0x02, + DAC960_PCI_Controller = 0x03, + DAC960_SCSItoSCSI_Controller = 0x08 + } __attribute__ ((packed)) ProductFamily; /* Byte 3 */ + } HardwareID; /* Bytes 0-3 */ + /* MajorVersion.MinorVersion-FirmwareType-TurnID */ + struct { + unsigned char MajorVersion; /* Byte 4 */ + unsigned char MinorVersion; /* Byte 5 */ + unsigned char TurnID; /* Byte 6 */ + char FirmwareType; /* Byte 7 */ + } FirmwareID; /* Bytes 4-7 */ + unsigned char :8; /* Byte 8 */ + unsigned int :24; /* Bytes 9-11 */ + unsigned char ConfiguredChannels; /* Byte 12 */ + unsigned char ActualChannels; /* Byte 13 */ + unsigned char MaxTargets; /* Byte 14 */ + unsigned char MaxTags; /* Byte 15 */ + unsigned char MaxLogicalDrives; /* Byte 16 */ + unsigned char MaxArms; /* Byte 17 */ + unsigned char MaxSpans; /* Byte 18 */ + unsigned char :8; /* Byte 19 */ + unsigned int :32; /* Bytes 20-23 */ + unsigned int MemorySize; /* Bytes 24-27 */ + unsigned int CacheSize; /* Bytes 28-31 */ + unsigned int FlashMemorySize; /* Bytes 32-35 */ + unsigned int NonVolatileMemorySize; /* Bytes 36-39 */ + struct { + enum { + DAC960_DRAM = 0x00, + DAC960_EDO = 0x01 + } __attribute__ ((packed)) RamType:3; /* Byte 40 Bits 0-2 */ + enum { + DAC960_None = 0x00, + DAC960_Parity = 0x01, + DAC960_ECC = 0x02 + } __attribute__ ((packed)) ErrorCorrection:3; /* Byte 40 Bits 3-5 */ + boolean FastPageMode:1; /* Byte 40 Bit 6 */ + boolean LowPowerMemory:1; /* Byte 40 Bit 7 */ + unsigned char :8; /* Bytes 41 */ + } MemoryType; + unsigned short ClockSpeed; /* Bytes 42-43 */ + unsigned short MemorySpeed; /* Bytes 44-45 */ + unsigned short HardwareSpeed; /* Bytes 46-47 */ + unsigned int :32; /* Bytes 48-51 */ + unsigned int :32; /* Bytes 52-55 */ + unsigned char :8; /* Byte 56 */ + unsigned char :8; /* Byte 57 */ + unsigned short :16; /* Bytes 58-59 */ + unsigned short MaxCommands; /* Bytes 60-61 */ + unsigned short MaxScatterGatherEntries; /* Bytes 62-63 */ + unsigned short MaxDriveCommands; /* Bytes 64-65 */ + unsigned short MaxIODescriptors; /* Bytes 66-67 */ + unsigned short MaxCombinedSectors; /* Bytes 68-69 */ + unsigned char Latency; /* Byte 70 */ + unsigned char :8; /* Byte 71 */ + unsigned char SCSITimeout; /* Byte 72 */ + unsigned char :8; /* Byte 73 */ + unsigned short MinFreeLines; /* Bytes 74-75 */ + unsigned int :32; /* Bytes 76-79 */ + unsigned int :32; /* Bytes 80-83 */ + unsigned char RebuildRateConstant; /* Byte 84 */ + unsigned char :8; /* Byte 85 */ + unsigned char :8; /* Byte 86 */ + unsigned char :8; /* Byte 87 */ + unsigned int :32; /* Bytes 88-91 */ + unsigned int :32; /* Bytes 92-95 */ + unsigned short PhysicalDriveBlockSize; /* Bytes 96-97 */ + unsigned short LogicalDriveBlockSize; /* Bytes 98-99 */ + unsigned short MaxBlocksPerCommand; /* Bytes 100-101 */ + unsigned short BlockFactor; /* Bytes 102-103 */ + unsigned short CacheLineSize; /* Bytes 104-105 */ + struct { + enum { + DAC960_Narrow_8bit = 0x00, + DAC960_Wide_16bit = 0x01, + DAC960_Wide_32bit = 0x02 + } __attribute__ ((packed)) BusWidth:2; /* Byte 106 Bits 0-1 */ + enum { + DAC960_Fast = 0x00, + DAC960_Ultra = 0x01, + } __attribute__ ((packed)) BusSpeed:2; /* Byte 106 Bits 2-3 */ + boolean Differential:1; /* Byte 106 Bit 4 */ + unsigned char :3; /* Byte 106 Bits 5-7 */ + } SCSICapability; + unsigned char :8; /* Byte 107 */ + unsigned int :32; /* Bytes 108-111 */ + unsigned short FirmwareBuildNumber; /* Bytes 112-113 */ + enum { + DAC960_AEMI = 0x01, + DAC960_OEM1 = 0x02, + DAC960_OEM2 = 0x04, + DAC960_OEM3 = 0x08, + DAC960_Conner = 0x10, + DAC960_SAFTE = 0x20 + } __attribute__ ((packed)) FaultManagementType; /* Byte 114 */ + unsigned char :8; /* Byte 115 */ + struct { + boolean Clustering:1; /* Byte 116 Bit 0 */ + boolean MylexOnlineRAIDExpansion:1; /* Byte 116 Bit 1 */ + unsigned int :30; /* Bytes 116-119 */ + } FirmwareFeatures; + unsigned int :32; /* Bytes 120-123 */ + unsigned int :32; /* Bytes 124-127 */ +} +DAC960_Enquiry2_T; + + +/* + Define the Get Logical Drive Information reply structure. +*/ + +typedef struct DAC960_LogicalDriveInformation +{ + unsigned int LogicalDriveSize; /* Bytes 0-3 */ + enum { + DAC960_LogicalDrive_Online = 0x03, + DAC960_LogicalDrive_Critical = 0x04, + DAC960_LogicalDrive_Offline = 0xFF + } __attribute__ ((packed)) LogicalDriveState; /* Byte 4 */ + unsigned char RAIDLevel:7; /* Byte 5 Bits 0-6 */ + boolean WriteBack:1; /* Byte 5 Bit 7 */ + unsigned int :16; /* Bytes 6-7 */ +} +DAC960_LogicalDriveInformation_T; + + +/* + Define the Perform Event Log Operation Types. +*/ + +typedef enum +{ + DAC960_GetEventLogEntry = 0x00 +} +__attribute__ ((packed)) +DAC960_PerformEventLogOpType_T; + + +/* + Define the Get Event Log Entry reply structure. +*/ + +typedef struct DAC960_EventLogEntry +{ + unsigned char MessageType; /* Byte 0 */ + unsigned char MessageLength; /* Byte 1 */ + unsigned char TargetID:5; /* Byte 2 Bits 0-4 */ + unsigned char Channel:3; /* Byte 2 Bits 5-7 */ + unsigned char LogicalUnit:6; /* Byte 3 Bits 0-5 */ + unsigned char :2; /* Byte 3 Bits 6-7 */ + unsigned short SequenceNumber; /* Bytes 4-5 */ + unsigned char ErrorCode:7; /* Byte 6 Bits 0-6 */ + boolean Valid:1; /* Byte 6 Bit 7 */ + unsigned char SegmentNumber; /* Byte 7 */ + unsigned char SenseKey:4; /* Byte 8 Bits 0-3 */ + unsigned char :1; /* Byte 8 Bit 4 */ + boolean ILI:1; /* Byte 8 Bit 5 */ + boolean EOM:1; /* Byte 8 Bit 6 */ + boolean Filemark:1; /* Byte 8 Bit 7 */ + unsigned char Information[4]; /* Bytes 9-12 */ + unsigned char AdditionalSenseLength; /* Byte 13 */ + unsigned char CommandSpecificInformation[4]; /* Bytes 14-17 */ + unsigned char AdditionalSenseCode; /* Byte 18 */ + unsigned char AdditionalSenseCodeQualifier; /* Byte 19 */ + unsigned char Dummy[12]; /* Bytes 20-31 */ +} +DAC960_EventLogEntry_T; + +#define DAC960_EventMessagesCount 13 + +static char + *DAC960_EventMessages[DAC960_EventMessagesCount] = + { "killed because write recovery failed", + "killed because of SCSI bus reset failure", + "killed because of double check condition", + "killed because it was removed", + "killed because of gross error on SCSI chip", + "killed because of bad tag returned from drive", + "killed because of timeout on SCSI command", + "killed because of reset SCSI command issued from system", + "killed because busy or parity error count exceeded limit", + "killed because of 'kill drive' command from system", + "killed because of selection timeout", + "killed due to SCSI phase sequence error", + "killed due to unknown status" }; + + +/* + Define the Get Device State reply structure. +*/ + +typedef struct DAC960_DeviceState +{ + boolean Present:1; /* Byte 0 Bit 0 */ + unsigned char :7; /* Byte 0 Bits 1-7 */ + enum { + DAC960_OtherType = 0x00, + DAC960_DiskType = 0x01, + DAC960_SequentialType = 0x02, + DAC960_CDROM_or_WORM_Type = 0x03 + } __attribute__ ((packed)) DeviceType:2; /* Byte 1 Bits 0-1 */ + boolean :1; /* Byte 1 Bit 2 */ + boolean Fast20:1; /* Byte 1 Bit 3 */ + boolean Sync:1; /* Byte 1 Bit 4 */ + boolean Fast:1; /* Byte 1 Bit 5 */ + boolean Wide:1; /* Byte 1 Bit 6 */ + boolean TaggedQueuingSupported:1; /* Byte 1 Bit 7 */ + enum { + DAC960_Device_Dead = 0x00, + DAC960_Device_WriteOnly = 0x02, + DAC960_Device_Online = 0x03, + DAC960_Device_Standby = 0x10 + } __attribute__ ((packed)) DeviceState; /* Byte 2 */ + unsigned char :8; /* Byte 3 */ + unsigned char SynchronousMultiplier; /* Byte 4 */ + unsigned char SynchronousOffset:5; /* Byte 5 Bits 0-4 */ + unsigned char :3; /* Byte 5 Bits 5-7 */ + unsigned long DiskSize __attribute__ ((packed)); /* Bytes 6-9 */ +} +DAC960_DeviceState_T; + + +/* + Define the Get Rebuild Progress reply structure. +*/ + +typedef struct DAC960_RebuildProgress +{ + unsigned int LogicalDriveNumber; /* Bytes 0-3 */ + unsigned int LogicalDriveSize; /* Bytes 4-7 */ + unsigned int RemainingBlocks; /* Bytes 8-11 */ +} +DAC960_RebuildProgress_T; + + +/* + Define the Config2 reply structure. +*/ + +typedef struct DAC960_Config2 +{ + unsigned char :1; /* Byte 0 Bit 0 */ + boolean ActiveNegationEnabled:1; /* Byte 0 Bit 1 */ + unsigned char :5; /* Byte 0 Bits 2-6 */ + boolean NoRescanIfResetReceivedDuringScan:1; /* Byte 0 Bit 7 */ + boolean StorageWorksSupportEnabled:1; /* Byte 1 Bit 0 */ + boolean HewlettPackardSupportEnabled:1; /* Byte 1 Bit 1 */ + boolean NoDisconnectOnFirstCommand:1; /* Byte 1 Bit 2 */ + unsigned char :2; /* Byte 1 Bits 3-4 */ + boolean AEMI_ARM:1; /* Byte 1 Bit 5 */ + boolean AEMI_OFM:1; /* Byte 1 Bit 6 */ + unsigned char :1; /* Byte 1 Bit 7 */ + enum { + DAC960_OEMID_Mylex = 0x00, + DAC960_OEMID_IBM = 0x08, + DAC960_OEMID_HP = 0x0A, + DAC960_OEMID_DEC = 0x0C, + DAC960_OEMID_Siemens = 0x10, + DAC960_OEMID_Intel = 0x12 + } __attribute__ ((packed)) OEMID; /* Byte 2 */ + unsigned char OEMModelNumber; /* Byte 3 */ + unsigned char PhysicalSector; /* Byte 4 */ + unsigned char LogicalSector; /* Byte 5 */ + unsigned char BlockFactor; /* Byte 6 */ + boolean ReadAheadEnabled:1; /* Byte 7 Bit 0 */ + boolean LowBIOSDelay:1; /* Byte 7 Bit 1 */ + unsigned char :2; /* Byte 7 Bits 2-3 */ + boolean ReassignRestrictedToOneSector:1; /* Byte 7 Bit 4 */ + unsigned char :1; /* Byte 7 Bit 5 */ + boolean ForceUnitAccessDuringWriteRecovery:1; /* Byte 7 Bit 6 */ + boolean EnableLeftSymmetricRAID5Algorithm:1; /* Byte 7 Bit 7 */ + unsigned char DefaultRebuildRate; /* Byte 8 */ + unsigned char :8; /* Byte 9 */ + unsigned char BlocksPerCacheLine; /* Byte 10 */ + unsigned char BlocksPerStripe; /* Byte 11 */ + struct { + enum { + DAC960_Async = 0x00, + DAC960_Sync_8MHz = 0x01, + DAC960_Sync_5MHz = 0x02, + DAC960_Sync_10or20MHz = 0x03 /* Bits 0-1 */ + } __attribute__ ((packed)) Speed:2; + boolean Force8Bit:1; /* Bit 2 */ + boolean DisableFast20:1; /* Bit 3 */ + unsigned char :3; /* Bits 4-6 */ + boolean EnableTaggedQueuing:1; /* Bit 7 */ + } __attribute__ ((packed)) ChannelParameters[6]; /* Bytes 12-17 */ + unsigned char SCSIInitiatorID; /* Byte 18 */ + unsigned char :8; /* Byte 19 */ + enum { + DAC960_StartupMode_ControllerSpinUp = 0x00, + DAC960_StartupMode_PowerOnSpinUp = 0x01 + } __attribute__ ((packed)) StartupMode; /* Byte 20 */ + unsigned char SimultaneousDeviceSpinUpCount; /* Byte 21 */ + unsigned char SecondsDelayBetweenSpinUps; /* Byte 22 */ + unsigned char Reserved1[29]; /* Bytes 23-51 */ + boolean BIOSDisabled:1; /* Byte 52 Bit 0 */ + boolean CDROMBootEnabled:1; /* Byte 52 Bit 1 */ + unsigned char :3; /* Byte 52 Bits 2-4 */ + enum { + DAC960_Geometry_128_32 = 0x00, + DAC960_Geometry_255_63 = 0x01, + DAC960_Geometry_Reserved1 = 0x02, + DAC960_Geometry_Reserved2 = 0x03 + } __attribute__ ((packed)) DriveGeometry:2; /* Byte 52 Bits 5-6 */ + unsigned char :1; /* Byte 52 Bit 7 */ + unsigned char Reserved2[9]; /* Bytes 53-61 */ + unsigned short Checksum; /* Bytes 62-63 */ +} +DAC960_Config2_T; + + +/* + Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count + structure. +*/ + +typedef struct DAC960_ScatterGatherSegment +{ + DAC960_BusAddress_T SegmentDataPointer; /* Bytes 0-3 */ + DAC960_ByteCount_T SegmentByteCount; /* Bytes 4-7 */ +} +DAC960_ScatterGatherSegment_T; + + +/* + Define the 13 Byte DAC960 Command Mailbox structure. Bytes 13-15 are + not used. The Command Mailbox structure is padded to 16 bytes for + efficient access. +*/ + +typedef union DAC960_CommandMailbox +{ + unsigned int Words[4]; /* Words 0-3 */ + unsigned char Bytes[16]; /* Bytes 0-15 */ + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy[14]; /* Bytes 2-15 */ + } __attribute__ ((packed)) Common; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Dummy1[6]; /* Bytes 2-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char Channel; /* Byte 2 */ + unsigned char TargetID; /* Byte 3 */ + unsigned char Dummy1[4]; /* Bytes 4-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3D; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + DAC960_PerformEventLogOpType_T OperationType; /* Byte 2 */ + unsigned char OperationQualifier; /* Byte 3 */ + unsigned short SequenceNumber; /* Bytes 4-5 */ + unsigned char Dummy1[2]; /* Bytes 6-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char Dummy2[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) Type3E; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + struct { + unsigned short TransferLength:11; /* Bytes 2-3 */ + unsigned char LogicalDriveNumber:5; /* Byte 3 Bits 3-7 */ + } __attribute__ ((packed)) LD; + unsigned int LogicalBlockAddress; /* Bytes 4-7 */ + DAC960_BusAddress_T BusAddress; /* Bytes 8-11 */ + unsigned char ScatterGatherCount:6; /* Byte 12 Bits 0-5 */ + enum { + DAC960_ScatterGather_32BitAddress_32BitByteCount = 0x0, + DAC960_ScatterGather_32BitAddress_16BitByteCount = 0x1, + DAC960_ScatterGather_32BitByteCount_32BitAddress = 0x2, + DAC960_ScatterGather_16BitByteCount_32BitAddress = 0x3 + } __attribute__ ((packed)) ScatterGatherType:2; /* Byte 12 Bits 6-7 */ + unsigned char Dummy[3]; /* Bytes 13-15 */ + } __attribute__ ((packed)) Type5; + struct { + DAC960_CommandOpcode_T CommandOpcode; /* Byte 0 */ + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 1 */ + unsigned char CommandOpcode2; /* Byte 2 */ + unsigned char :8; /* Byte 3 */ + DAC960_BusAddress_T CommandMailboxesBusAddress; /* Bytes 4-7 */ + DAC960_BusAddress_T StatusMailboxesBusAddress; /* Bytes 8-11 */ + unsigned char Dummy[4]; /* Bytes 12-15 */ + } __attribute__ ((packed)) TypeX; +} +DAC960_CommandMailbox_T; + + +/* + Define the DAC960 V4 Controller Command Mailbox structure. +*/ + +typedef DAC960_CommandMailbox_T DAC960_V4_CommandMailbox_T; + + +/* + Define the DAC960 V4 Controller Status Mailbox structure. +*/ + +typedef union DAC960_V4_StatusMailbox +{ + unsigned int Word; /* Bytes 0-3 */ + struct { + DAC960_CommandIdentifier_T CommandIdentifier; /* Byte 0 */ + unsigned char :7; /* Byte 1 Bits 0-6 */ + boolean Valid:1; /* Byte 1 Bit 7 */ + DAC960_CommandStatus_T CommandStatus; /* Bytes 2-3 */ + } Fields; +} +DAC960_V4_StatusMailbox_T; + + +/* + Define the DAC960 Driver Command Types. +*/ + +typedef enum +{ + DAC960_ReadCommand = 1, + DAC960_WriteCommand = 2, + DAC960_ReadRetryCommand = 3, + DAC960_WriteRetryCommand = 4, + DAC960_MonitoringCommand = 5, + DAC960_ImmediateCommand = 6 +} +DAC960_CommandType_T; + + +/* + Define the DAC960 Driver Command structure. +*/ + +typedef struct DAC960_Command +{ + DAC960_CommandType_T CommandType; + DAC960_CommandMailbox_T CommandMailbox; + DAC960_CommandStatus_T CommandStatus; + struct DAC960_Controller *Controller; + struct DAC960_Command *Next; + Semaphore_T *Semaphore; + unsigned int LogicalDriveNumber; + unsigned int BlockNumber; + unsigned int BlockCount; + unsigned int SegmentCount; + BufferHeader_T *BufferHeader; + DAC960_ScatterGatherSegment_T + ScatterGatherList[DAC960_MaxScatterGatherSegments]; +} +DAC960_Command_T; + + +/* + Define the DAC960 Driver Controller structure. +*/ + +typedef struct DAC960_Controller +{ + void *BaseAddress; + void *MemoryMappedAddress; + DAC960_ControllerType_T ControllerType; + DAC960_IO_Address_T IO_Address; + DAC960_PCI_Address_T PCI_Address; + unsigned char ControllerNumber; + unsigned char ModelName[12]; + unsigned char FullModelName[18]; + unsigned char FirmwareVersion[14]; + unsigned char Bus; + unsigned char Device; + unsigned char Function; + unsigned char IRQ_Channel; + unsigned char Channels; + unsigned char MemorySize; + unsigned char LogicalDriveCount; + unsigned char GeometryTranslationHeads; + unsigned char GeometryTranslationSectors; + unsigned short ControllerQueueDepth; + unsigned short DriverQueueDepth; + unsigned short MaxBlocksPerCommand; + unsigned short MaxScatterGatherSegments; + unsigned short StripeSize; + unsigned short SegmentSize; + unsigned short NewEventLogSequenceNumber; + unsigned short OldEventLogSequenceNumber; + unsigned short MessageBufferLength; + unsigned int ControllerUsageCount; + unsigned int EnquiryIndex; + unsigned int LogicalDriveInformationIndex; + unsigned int DeviceStateIndex; + unsigned int DeviceStateChannel; + unsigned int DeviceStateTargetID; + unsigned long SecondaryMonitoringTime; + unsigned long RebuildLastReportTime; + boolean SAFTE_FaultManagementEnabled; + boolean MonitoringCommandDeferred; + boolean NeedLogicalDriveInformation; + boolean NeedDeviceStateInformation; + boolean NeedRebuildProgress; + GenericDiskInfo_T GenericDiskInfo; + Timer_T MonitoringTimer; + DAC960_Command_T *FreeCommands; + DAC960_V4_CommandMailbox_T *FirstCommandMailbox; + DAC960_V4_CommandMailbox_T *LastCommandMailbox; + DAC960_V4_CommandMailbox_T *NextCommandMailbox; + DAC960_V4_CommandMailbox_T *PreviousCommandMailbox; + DAC960_V4_StatusMailbox_T *FirstStatusMailbox; + DAC960_V4_StatusMailbox_T *LastStatusMailbox; + DAC960_V4_StatusMailbox_T *NextStatusMailbox; + DAC960_Enquiry_T Enquiry[2]; + DAC960_LogicalDriveInformation_T + LogicalDriveInformation[2][DAC960_MaxLogicalDrives]; + DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets]; + DAC960_EventLogEntry_T EventLogEntry; + DAC960_RebuildProgress_T RebuildProgress; + DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth]; + DiskPartition_T DiskPartitions[DAC960_MinorCount]; + int LogicalDriveUsageCount[DAC960_MaxLogicalDrives]; + int PartitionSizes[DAC960_MinorCount]; + int BlockSizes[DAC960_MinorCount]; + int MaxSectorsPerRequest[DAC960_MinorCount]; + int MaxSegmentsPerRequest[DAC960_MinorCount]; + char MessageBuffer[DAC960_MessageBufferSize]; +} +DAC960_Controller_T; + + +/* + DAC960_AcquireControllerLock acquires exclusive access to Controller. +*/ + +static inline +void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ + save_flags(*ProcessorFlags); + cli(); +} + + +/* + DAC960_ReleaseControllerLock releases exclusive access to Controller. +*/ + +static inline +void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ + restore_flags(*ProcessorFlags); +} + + +/* + DAC960_AcquireControllerLockRF acquires exclusive access to Controller, + but is only called from the request function when interrupts are disabled. +*/ + +static inline +void DAC960_AcquireControllerLockRF(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_ReleaseControllerLockRF releases exclusive access to Controller, + but is only called from the request function when interrupts are disabled. +*/ + +static inline +void DAC960_ReleaseControllerLockRF(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_AcquireControllerLockIH acquires exclusive access to Controller, + but is only called from the interrupt handler when interrupts are disabled. +*/ + +static inline +void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + DAC960_ReleaseControllerLockIH releases exclusive access to Controller, + but is only called from the interrupt handler when interrupts are disabled. +*/ + +static inline +void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller, + ProcessorFlags_T *ProcessorFlags) +{ +} + + +/* + Define inline functions to provide an abstraction for reading and writing the + DAC960 V4 Controller Interface Registers. +*/ + +static inline +void DAC960_V4_NewCommand(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.NewCommand = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_AcknowledgeStatus(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.AcknowledgeStatus = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V4_SoftReset(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.SoftReset = true; + writel(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V4_MailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.MailboxFull; +} + +static inline +void DAC960_V4_AcknowledgeInterrupt(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true; + writel(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V4_StatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.StatusAvailable; +} + +static inline +void DAC960_V4_EnableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3; + InterruptMaskRegister.Bits.DisableInterrupts = false; + InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F; + writel(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); +} + +static inline +void DAC960_V4_DisableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = 0; + InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3; + InterruptMaskRegister.Bits.DisableInterrupts = true; + InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F; + writel(InterruptMaskRegister.All, + ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); +} + +static inline +boolean DAC960_V4_InterruptsEnabledP(void *ControllerBaseAddress) +{ + DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister; + InterruptMaskRegister.All = + readl(ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset); + return !InterruptMaskRegister.Bits.DisableInterrupts; +} + +static inline +void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox, + DAC960_CommandMailbox_T *CommandMailbox) +{ + NextCommandMailbox->Words[1] = CommandMailbox->Words[1]; + NextCommandMailbox->Words[2] = CommandMailbox->Words[2]; + NextCommandMailbox->Words[3] = CommandMailbox->Words[3]; + NextCommandMailbox->Words[0] = CommandMailbox->Words[0]; +} + +static inline +void DAC960_V4_WriteLegacyCommand(void *ControllerBaseAddress, + DAC960_CommandMailbox_T *CommandMailbox) +{ + writel(CommandMailbox->Words[0], + ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset); + writel(CommandMailbox->Words[1], + ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset); + writel(CommandMailbox->Words[2], + ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset); + writeb(CommandMailbox->Bytes[12], + ControllerBaseAddress + DAC960_V4_MailboxRegister12Offset); +} + +static inline DAC960_CommandIdentifier_T +DAC960_V4_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +{ + return readb(ControllerBaseAddress + + DAC960_V4_StatusCommandIdentifierRegOffset); +} + +static inline DAC960_CommandStatus_T +DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress) +{ + return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset); +} + + +/* + Define inline functions to provide an abstraction for reading and writing the + DAC960 V3 Controller Interface Registers. +*/ + +static inline +void DAC960_V3_NewCommand(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.NewCommand = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V3_AcknowledgeStatus(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.AcknowledgeStatus = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +void DAC960_V3_SoftReset(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = 0; + InboundDoorBellRegister.Write.SoftReset = true; + writeb(InboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress) +{ + DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister; + InboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset); + return InboundDoorBellRegister.Read.MailboxFull; +} + +static inline +void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress) +{ + DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = 0; + OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true; + writeb(OutboundDoorBellRegister.All, + ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset); +} + +static inline +boolean DAC960_V3_StatusAvailableP(void *ControllerBaseAddress) +{ + DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister; + OutboundDoorBellRegister.All = + readb(ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset); + return OutboundDoorBellRegister.Read.StatusAvailable; +} + +static inline +void DAC960_V3_EnableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = 0; + InterruptEnableRegister.Bits.EnableInterrupts = true; + writeb(InterruptEnableRegister.All, + ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); +} + +static inline +void DAC960_V3_DisableInterrupts(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = 0; + InterruptEnableRegister.Bits.EnableInterrupts = false; + writeb(InterruptEnableRegister.All, + ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); +} + +static inline +boolean DAC960_V3_InterruptsEnabledP(void *ControllerBaseAddress) +{ + DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister; + InterruptEnableRegister.All = + readb(ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset); + return InterruptEnableRegister.Bits.EnableInterrupts; +} + +static inline +void DAC960_V3_WriteCommandMailbox(void *ControllerBaseAddress, + DAC960_CommandMailbox_T *CommandMailbox) +{ + writel(CommandMailbox->Words[0], + ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset); + writel(CommandMailbox->Words[1], + ControllerBaseAddress + DAC960_V3_MailboxRegister4Offset); + writel(CommandMailbox->Words[2], + ControllerBaseAddress + DAC960_V3_MailboxRegister8Offset); + writeb(CommandMailbox->Bytes[12], + ControllerBaseAddress + DAC960_V3_MailboxRegister12Offset); +} + +static inline DAC960_CommandIdentifier_T +DAC960_V3_ReadStatusCommandIdentifier(void *ControllerBaseAddress) +{ + return readb(ControllerBaseAddress + + DAC960_V3_StatusCommandIdentifierRegOffset); +} + +static inline DAC960_CommandStatus_T +DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress) +{ + return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset); +} + + +/* + Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses + and PCI Bus Addresses. +*/ + +static inline DAC960_BusAddress_T Virtual_to_Bus(void *VirtualAddress) +{ + return (DAC960_BusAddress_T) virt_to_bus(VirtualAddress); +} + +static inline void *Bus_to_Virtual(DAC960_BusAddress_T BusAddress) +{ + return (void *) bus_to_virt(BusAddress); +} + + +/* + Define compatibility macros between Linux 2.0 and Linux 2.1. +*/ + +#if LINUX_VERSION_CODE < 0x20100 + +#define MODULE_PARM(Variable, Type) +#define ioremap_nocache(Offset, Size) vremap(Offset, Size) +#define iounmap(Address) vfree(Address) + +#endif + + +/* + Define prototypes for the forward referenced DAC960 Driver Internal Functions. +*/ + +static void DAC960_RequestFunction0(void); +static void DAC960_RequestFunction1(void); +static void DAC960_RequestFunction2(void); +static void DAC960_RequestFunction3(void); +static void DAC960_RequestFunction4(void); +static void DAC960_RequestFunction5(void); +static void DAC960_RequestFunction6(void); +static void DAC960_RequestFunction7(void); +static void DAC960_InterruptHandler(int, void *, Registers_T *); +static void DAC960_QueueMonitoringCommand(DAC960_Command_T *); +static void DAC960_MonitoringTimerFunction(unsigned long); +static int DAC960_Open(Inode_T *, File_T *); +static void DAC960_Release(Inode_T *, File_T *); +static int DAC960_Ioctl(Inode_T *, File_T *, unsigned int, unsigned long); +static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *); +static void DAC960_Message(DAC960_MessageLevel_T, char *, + DAC960_Controller_T *, ...); + + +#endif /* DAC960_DriverVersion */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- linux.vanilla/drivers/scsi/Makefile Sun Dec 6 00:14:42 1998 +++ linux/drivers/scsi/Makefile Fri Dec 11 19:10:10 1998 @@ -145,6 +145,22 @@ endif endif +ifeq ($(CONFIG_SCSI_ACARD),y) +L_OBJS += atp870u.o +else + ifeq ($(CONFIG_SCSI_ACARD),m) + M_OBJS += atp870u.o + endif +endif + +ifeq ($(CONFIG_SCSI_INITIO),y) +L_OBJS += initio.o +else + ifeq ($(CONFIG_SCSI_INITIO),m) + M_OBJS += initio.o + endif +endif + ifeq ($(CONFIG_SCSI_AHA152X),y) L_OBJS += aha152x.o @@ -401,6 +417,12 @@ g_NCR5380.o: g_NCR5380.c $(CC) $(CFLAGS) -DGENERIC_NCR5380_OVERRIDE="{{(NCR5380_map_type)0x350,5,0, BOARD_NCR53C400}};" -c g_NCR5380.c + +initio.o: ini9100u.c i91uscsi.c + $(CC) $(CFLAGS) -c ini9100u.c -o ini9100u.o + $(CC) $(CFLAGS) -c i91uscsi.c -o i91uscsi.o + $(LD) -r -o initio.o ini9100u.o i91uscsi.o + rm -f ini9100u.o i91uscsi.o megaraid.o: megaraid.c $(CC) $(CFLAGS) -c megaraid.c diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/README.tmscsim linux/drivers/scsi/README.tmscsim --- linux.vanilla/drivers/scsi/README.tmscsim Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/README.tmscsim Sun Dec 6 03:15:09 1998 @@ -0,0 +1,414 @@ +The tmscsim driver +================== + +1. Purpose and history +2. Installation +3. Features +4. Configuration via /proc/scsi/tmscsim/? +5. Configuration via boot/module params +6. Potential improvements +7. Bug reports, debugging and updates +8. Acknowledgements + + +1. Purpose and history +---------------------- +The tmscsim driver supports PCI SCSI Host Adapters based on the AM53C974 +chip. AM53C974 based SCSI adapters include: + Tekram DC390, DC390T + Dawicontrol 2974 + QLogic Fast! PCI Basic + some on-board adapters +(This is most probably not a complete list) + +It has originally written by C.L. Huang from the Tekram corp. to support the +Tekram DC390(T) adapter. This is where the name comes from: tm = Tekram +scsi = SCSI driver, m = AMD (?) as opposed to w for the DC390W/U/F +(NCR53c8X5, X=2/7) driver. Yes, there was also a driver for the latter, +tmscsimw, which supported DC390W/U/F adapters. It's not maintained any more, +as the ncr53c8xx is perfectly supporting these adpaters since some time. + +The driver first appeared in April 1996, exclusively supported the DC390 +and has been enhanced since then in various steps. In May 1998 support for +general AM53C974 based adapters and some possibilities to configure it were +added. The non-DC390 support works by assuming some values for the data +normally taken from the DC390 EEPROM. See below (chapter 5) for details. + +When using the DC390, the configuration is still be done using the DC390 +BIOS setup. The DC390 EEPROM is read and used by the driver, any boot or +module parameters (chapter 5) are ignored! However, you can change settings +dynamically, as described in chapter 4. + +For a more detailed description of the driver's history, see the first lines +of tmscsim.c. +The numbering scheme isn't consistent. The first versions went from 1.00 to +1.12, then 1.20a to 1.20t. Finally I decided to use the ncr53c8xx scheme. So +the next revisions will be 2.0a to 2.0X (stable), 2.1a to 2.1X (experimental), +2.2a to 2.2X (stable, again) etc. (X = anything between a and z.) If I send +fixes to people for testing, those will have a digit appended, e.g. 2.0a1. + + +2. Installation +--------------- +If you got any recent kernel with this driver and document included in +linux/drivers/scsi, you basically have to do nothing special to use this +driver. Of course you have to choose to compile SCSI support and DC390(T) +support into your kernel or as module when configuring your kernel for +compiling. + +If you got an older kernel with an old version of this driver included, you +should copy the files (dc390.h, tmscsim.h, tmscsim.c, scsiiom.c and +README.tmscsim) from this directory to linux/drivers/scsi. You have to +recompile your kernel/module of course. + +You should apply the three patches included in dc390-20-kernel.diff +(Applying them: cd /usr/src; patch -p0 <~/dc390-20-kernel.diff) +The patches are against 2.1.103, so you might have to manually resolve +rejections when applying to another kernel version. + +The patches will update the kernel startup code to allow boot parameters to +be passed to the driver, update the Documentation and finally offer you the +possibility to omit the non-DC390 parts of the driver. +(By selecting "Omit support for non DC390" you basically disable the +emulation of a DC390 EEPROM for non DC390 adapters. This saves a few bytes +of memory.) + +If you got a very old kernel without the tmscsim driver (pre 2.0.31) +I recommend upgrading your kernel. However, if you don't want to, please +contact me to get the appropriate patches. + +Testing a SCSI driver is always a delicate thing to do. The 2.0 driver has +proven stable on many systems, but it's still a good idea to take some +precautions. In an ideal world you would have a full backup of your disks. +The world isn't ideal and most people don't have full backups (me neither). +So take at least the following two measures: +* make your kernel remount the FS read-only on detecting an error: + tune2fs -e remount-ro /dev/sd?? +* have copies of your SCSI disk's partition tables on some safe location: + dd if=/dev/sda of=/mnt/floppy/sda bs=512 count=1 +* make sure you are able to boot Linux (e.g. from floppy disk using InitRD) + if your SCSI disk gets corrupted. You can use + ftp://student.physik.uni-dortmund.de/pub/linux/kernel/bootdisk.gz + +One more warning: I used to overclock my PCI bus to 41.67 MHz. My Tekram +DC390F (Sym53c875) accepted this as well as my Millenium. But the Am53C974 +produced errors and started to corrupt my disks. So don't do that! A 37.50 +MHz PCI bus works for me, though, but I don't recommend using higher clocks +than the 33.33 MHz being in the PCI spec. + +If you want to share the IRQ with another device and the driver refuses to +do, you might succeed with changing the DC390_IRQ type in tmscsim.c to +SA_SHIRQ | SA_INTERRUPT. + + +3.Features +---------- +- SCSI + * Tagged queueing + * Sync speed up to 10 MHz + * Disconnection + * Multiple LUNs + +- General / Linux interface + * Support for up to 4 adapters. + * DC390 EEPROM usage or boot/module params + * Information via cat /proc/scsi/tmscsim/? + * Dynamically configurable by writing to /proc/scsi/tmscsim/? + * Dynamic allocation of resources + * SMP support: Adapter specific locks (Linux 2.1.x) + * Uniform source code for Linux-2.x.y + * Support for dyn. addition/removal of devices via add/remove-single-device + (Try: echo "scsi add-single-device H C I L" >/proc/scsi/scsi + H = Host, C = Channel, I = SCSI ID, L = SCSI LUN.) Use with care! + * Try to use the partition table for the determination of the mapping + + +4. Configuration via /proc/scsi/tmscsim/? +----------------------------------------- +First of all look at the output of /proc/scsi/tmscsim/? by typing + cat /proc/scsi/tmscsim/? +The "?" should be replaced by the SCSI host number. (The shell might do this +for you.) +You will see some info regarding the adapter and, at the end, a listing of +the attached devices and their settings. + +Here's an example: +garloff@kg1:/home/garloff > cat /proc/scsi/tmscsim/0 +Tekram DC390/AM53C974 PCI SCSI Host Adapter, Driver Version 1.20s, 1998/08/20 +SCSI Host Nr 0, AM53C974 Adapter Nr 0 +IOPortBase 0x6200, IRQLevel 0x09 +MaxID 7, MaxLUN 8, AdapterID 7, SelTimeout 250 ms +TagMaxNum 16, Status 0, ACBFlag 0, GlitchEater 24 ns +Statistics: Nr of Cmnds 39563, Cmnds not sent directly 0, Out of SRB conds 0 + Nr of lost arbitrations 17 +Nr of attached devices: 4, Nr of DCBs: 4 +Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs +00 00 00 Yes Yes Yes Yes Yes No 100 ns 10.0 M 15 +01 01 00 Yes Yes Yes Yes Yes No 100 ns 10.0 M 15 +02 03 00 Yes Yes Yes Yes No No 100 ns 10.0 M 15 +03 05 00 Yes No Yes Yes No No (200 ns) + +Note that the settings MaxID and MaxLUN are not zero- but one-based, which +means that a setting MaxLUN=4, will result in the support of LUNs 0..3. This +is somehow inconvenient, but the way the mid-level SCSI code expects it to be. + +ACB and DCB are acronyms for Adapter Control Block and Device Control Block. +These are data structures of the driver containing information about the +adapter and the connected SCSI devices respectively. + +Idx is the device index (just a consecutive number for the driver), ID and +LUN are the SCSI ID and LUN, Prty means Parity checking, Sync synchronous +negotiation, DsCn Disconnection, SndS Send Start command on startup (not +used by the driver) and TagQ Tagged Command Queueing. NegoPeriod and +SyncSpeed are somehow redundant, because they are reciprocal values +(1 / 112 ns = 8.9 MHz). At least in theory. The driver is able to adjust the +NegoPeriod more accurate (4ns) than the SyncSpeed (1 / 25ns). I don't know +if certain devices will have problems with this discrepancy. Max. speed is +10 MHz corresp. to a min. NegoPeriod of 100 ns. +(The driver allows slightly higher speeds if the devices (Ultra SCSI) accept +it, but that's out of adapter spec, on your own risk and unlikely to improve +performance. You're likely to crash your disks.) +SyncOffs is the offset used for synchronous negotiations; max. is 15. +The last values are only shown, if Sync is enabled. (NegoPeriod is still +displayed in brackets to show the values which will be used after enabling +Sync.) +The STOP parameter is for testing/debugging purposes only and should bet set +to No. Please don't fiddle with it, unless you want to get rid of the +contents of your disk. + +If you want to change a setting, you can do that by writing to +/proc/scsi/tmscsim/?. Basically you have to imitate the output of driver. +(Don't use the brackets for NegoPeriod on Sync disabled devices.) +You don't have to care about capitalisation. The driver will accept space, +tab, comma, = and : as separators. + +There are three kinds of changes: + +(1) Change driver settings: + You type the names of the parameters and the params following it. + Example: + echo "MaxLUN=8 seltimeout 200" >/proc/scsi/tmscsim/0 + + Note that you can only change MaxID, MaxLUN, AdapterID, SelTimeOut, + TagMaxNum, ACBFlag and GlitchEater. Don't change ACBFlag unless you + want to see what happens, if the driver hangs. + +(2) Change device settings: You write a config line to the driver. The Nr + must match the ID and LUN given. If you give "-" as parameter, it is + ignored and the corresponding setting won't be changed. + You can use "y" or "n" instead of "Yes" and "No" if you want to. + You don't need to specify a full line. The driver automatically performs + an INQUIRY on the device if necessary to check if it is capable to operate + with the given settings (Sync, TagQ). + Examples: + echo "0 0 0 y y y - y - 10" >/proc/scsi/tmscsim/0 + echo "3 5 0 y n y" >/proc/scsi/tmscsim/0 + + To give a short explanation of the first example: + The first three numbers, "0 0 0" (Device index 0, SCSI ID 0, SCSI LUN 0), + select the device to which the following parameters apply. Note that it + would be sufficient to use the index or both SCSI ID and LUN, but I chose + to require all three to have a syntax similar to the output. + The following "y y y - y" enables Parity checking, enables Synchronous + transfers, Disconnection, leaves Send Start (not used) untouched and + enables Tagged Command Queueing for the selected device. The "-" skips + the Negotiation Period setting but the "10" sets the max sync. speed to + 10 MHz. It's useless to specify both NegoPeriod and SyncSpeed as + discussed above. The values used in this example will result in maximum + performance. + +(3) Special commands: You can force a SCSI bus reset, an INQUIRY command and + the removal of a device's DCB. + This is only used for debugging when you meet problems. The parameter of + the INQUIRY and remove command is the device index as shown by the + output of /proc/scsi/tmscsim/? in the device listing in the first column + (Idx). + Examples: + echo "reset" >/proc/scsi/tmscsim/0 + echo "inquiry 1" >/proc/scsi/tmscsim/0 + echo "remove 2" >/proc/scsi/tmscsim/1 + + Note that you will meet problems when you remove a device's DCB with the + remove command if it contains partitions which are mounted. Only use it + after unmounting its partitions, telling the SCSI mid-level code to + remove it (scsi remove-single-device) and you really need a few bytes of + memory. + + +I'd suggest reviewing the output of /proc/scsi/tmscsim/? after changing +settings to see if everything changed as requested. + + +5. Configuration via boot/module parameters +------------------------------------------- +With the DC390, the driver reads its EEPROM settings and IGNORES boot / +module parameters. If you want to override the EEPROM settings of a DC390, +you have to use the /proc/scsi/tmscsim/? interface described in the above +chapter. + +However, if you do have another AM53C974 based adapter you might want to +adjust some settings before you are able to write to the /proc/scsi/tmscsim/? +pseudo-file, e.g. if you want to use another adapter ID than 7. (Note that +the log message "DC390: No EEPROM found!" is normal without a DC390.) +For this purpose, you can pass options to the driver before it is initialised +by using kernel or module parameters. See lilo(8) or modprobe(1) manual +pages on how to pass params to the kernel or a module. + +The syntax of the params is much shorter than the syntax of the /proc/... +interface. This makes it a little bit more difficult to use. However, long +parameter lines have the risk to be misinterpreted and the length of kernel +parameters is limited. + +As the support for non-DC390 adapters works by simulating the values of the +DC390 EEPROM, the settings are given in a DC390 BIOS' way. + +Here's the syntax: +tmscsim=AdaptID,SpdIdx,DevMode,AdaptMode,TaggedCmnds + +Each of the parameters is a number, containing the described information: + +* AdaptID: The SCSI ID of the host adapter. Must be in the range 0..7 + Default is 7. + +* SpdIdx: The index of the maximum speed as in the DC390 BIOS. The values + 0..7 mean 10, 8.0, 6.7, 5.7, 5.0, 4.0, 3.1 and 2 MHz resp. Default is + 1 (8.0 MHz). + +* DevMode is a bit mapped value describing the per-device features. It + applies to all devices. (Sync, Disc and TagQ will only apply, if the + device supports it.) The meaning of the bits (* = default): + + Bit Val(hex) Val(dec) Meaning + *0 0x01 1 Parity check + *1 0x02 2 Synchronous Negotiation + *2 0x04 4 Disconnection + *3 0x08 8 Send Start command on startup. (Not used) + *4 0x10 16 Tagged Queueing + + As usual, the desired value is obtained by adding the wanted values. If + you want to enable all values, e.g., you would use 31(0x1f). Default is 31. + +* AdaptMode is a bit mapped value describing the enabled adapter features. + + Bit Val(hex) Val(dec) Meaning + *0 0x01 1 Support more than two drives. (Not used) + *1 0x02 2 Use DOS compatible mapping for HDs greater than 1GB. + *2 0x04 4 Reset SCSI Bus on startup. + *3 0x08 8 Active Negation: Improves SCSI Bus noise immunity. + 4 0x10 16 Immediate return on BIOS seek command. (Not used) + (*)5 0x20 32 Check for LUNs >= 1. + + The default for LUN Check depends on CONFIG_SCSI_MULTI_LUN. + +* TaggedCmnds is a number indicating the maximum number of Tagged Commands. + It is the binary logarithm - 1 of the actual number. Max is 4 (32). + Value Number of Tagged Commands + 0 2 + 1 4 + 2 8 + *3 16 + 4 32 + +Example: + modprobe tmscsim tmscsim=6,2,31 +would set the adapter ID to 6, max. speed to 6.7 MHz, enable all device +features and leave the adapter features and the number of Tagged Commands +to the defaults. + +As you can see, you don't need to specify all of the five params. + +The defaults (7,1,31,15,3) are aggressive to allow good performance. You can +use tmscsim=7,0,31,63,4 for maximum performance, if your SCSI chain is +perfect. If you meet problems, you can use tmscsim=-1 which is a shortcut +for tmscsim=7,4,9,15,2. + + +6. Potential improvements +------------------------- +Most of the intended work on the driver has been done. Here are a few ideas +to further improve its usability: + +* More intelligent abort() routine +* Implement new_eh code (Linux-2.1+) +* Have the mid-level code (and not the driver) handle more of the various + conditions. +* Rework command queueing in the driver +* More user friendly boot/module param syntax + +Further investigation on these problems: + +* TagQ and Disconnection (Resel: SRB Tag Seleection) +* Problems with IRQ sharing (IO-APIC on SMP Systems) (??) +* Driver crashes with readcdda (xcdroast) + +Known problems: + +* There was a report that with a certain Scanner, the last SCSI command + won't be finished correctly. This might be a command queueing bug or a bug + in SCSI implementation of the scanner. Issueing another command to the + scanner seems to help. (Try echo "INQUIRY x" >/proc/scsi/tmscsim/?, where + x is the index (not the SCSI ID!) of the scanner. See 4.(3).) +* If there is a valid partition table, the driver will use it for determing + the mapping. Other operating systems may not like this mapping, though + it's consistent with the BIOS' behaviour. Old DC390 drivers ignored the + partition table and used a H/S = 64/32 or 255/63 translation. So if you + want to be compatible to those, use this old mapping when creating + partition tables. +* In some situations, the driver will get stuck in an abort loop. Please + disable DsCn, if you meet this problem. Please contact me for further + debugging. +* 2.1.115+: Linux misses locks in sr_ioctl.c and scsi_ioctl.c + There used to be a patch included here, which partially solved the + problem. I suggest you contact Chiaki Ishikawa , + Richard Waltham or Doug Ledford + , if you want to help further debugging it. +* 2.0.35: CD changers (e.g. NAKAMICHI MBR-7.{0,2}) have problems because + the mid-level code doesn't handle BLIST_SINGLELUN correctly. Apply + the patch 2035-scsi-singlelun.diff. Thanks to Chiaki Ishikawa. + I was told that this fix will be in 2.0.36, so you don't need it for + 2.0.36. +[The patch file is contained in the dc390-XXX.tar.gz files which can be found +on the ftp server. See below.] + + +7. Bug reports, debugging and updates +------------------------------------- +Whenever you have problems with the driver, you are invited to ask the +author for help. However, I'd suggest reading the docs and trying to solve +the problem yourself, first. +If you find something, which you believe to be a bug, please report it to me. +Please append the output of /proc/scsi/scsi, /proc/scsi/tmscsim/? and +maybe the DC390 log messages to the report. + +Bug reports should be send to me (Kurt Garloff ) as well +as to the linux-scsi list (), as sometimes bugs +are caused by the SCSI mid-level code. + +I will ask you for some more details and probably I will also ask you to +enable some of the DEBUG options in the driver (tmscsim.c:DC390_DEBUGXXX +defines). The driver will produce some data for the syslog facility then. +Beware: If your syslog gets written to a SCSI disk connected to your +AM53C974, the logging might produce log output again, and you might end +having your box spending most of its time doing the logging. + +The latest version of the driver can be found at: +ftp://student.physik.uni-dortmund.de/pub/linux/kernel/dc390/ + + +8. Acknowledgements +------------------- +Thanks to Linus Torvalds, Alan Cox, David Miller, Rik v. Riel, the FSF +people, the XFree86 team and all the others for the wonderful OS and +software. +Thanks to C.L. Huang and Philip Giang (Tekram) for the initial driver +release and support. +Thanks to Doug Ledford, Gerard Roudier for support with SCSI coding. +Thanks to a lot of people (espec. Chiaki Ishikawa, Andreas Haumer, Hubert +Tonneau) for intensively testing the driver (and even risking data loss +doing this during early revisions). + + +------------------------------------------------------------------------- +Written by Kurt Garloff 1998/06/11 +Last updated 1998/10/15, driver revision 2.0b +$Id: README.tmscsim,v 2.5 1998/11/05 10:38:38 garloff Exp $ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/RELEASE_NOTES.DAC960 linux/drivers/scsi/RELEASE_NOTES.DAC960 --- linux.vanilla/drivers/scsi/RELEASE_NOTES.DAC960 Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/RELEASE_NOTES.DAC960 Mon Nov 30 07:59:59 1998 @@ -0,0 +1,74 @@ + Mylex DAC960 PCI RAID Controller Driver for Linux + + Version 2.0.0 for Linux 2.0.36 + Version 2.1.0 for Linux 2.1.130 + + BETA TEST RELEASE 3 + + 23 November 1998 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1998 by Leonard N. Zubkoff + + + RELEASE NOTES + +o The error reporting functionality has been greatly improved. The driver now + queries the controller every 7 seconds to collect status information, and + reports on progress rebuilding logical drives at most every 60 seconds. + The status of all physical disk drives is also queried as well as updates + to the controller's internal error log. This functionality allows for + more accurate reporting of the cause of drive failures when they occur. + +o The production model AcceleRAID controllers are now supported. Mylex had + unfortunately provided me with pre-production AcceleRAID units which differed + in critical ways from the production models, and so the Beta2 driver release + did not function properly. + +o New releases of the Linux Symbios driver now contain code to notice when + motherboard Symbios chips are controlled by an AcceleRAID and to not + interfere with operation of the AcceleRAID board. A patch is also available + for the Symbios driver version 3.0i. + +o A bug was corrected where functionality necessary for swapping to files was + not implemented. + + + + + Mylex DAC960 PCI RAID Controller Driver for Linux + + Version 2.0.0 for Linux 2.0.36 + Version 2.1.0 for Linux 2.1.126 + + BETA TEST RELEASE 2 + + 19 October 1998 + + Leonard N. Zubkoff + Dandelion Digital + lnz@dandelion.com + + Copyright 1998 by Leonard N. Zubkoff + + + RELEASE NOTES + +o The Mylex AcceleRAID 250 (DAC960PTL-1) and AcceleRAID 200 (DAC960PTL-0) + models are now supported. Please note that if you use an AcceleRAID 200, + or an AcceleRAID 250 configured to use the Symbios chips on the motherboard, + then the Symbios driver must not be included in the Linux kernel. This + restriction will be removed in a future release of these drivers. + +o This version of the DAC960 driver uses a more efficient interface for + communicating with the AcceleRAID 250, AcceleRAID 200, DAC960PJ, and + DAC960PG models. + +o A bug was corrected where the cache was not flushed at shutdown on systems + with a DAC960 Logical Drive mounted as the root file system. This only + affected systems where the DAC960 Logical Drive was configured for write back + operation without a battery backup unit installed and the system was powered + off within a couple of seconds of system shutdown. diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- linux.vanilla/drivers/scsi/aic7xxx.c Sun Dec 6 00:14:43 1998 +++ linux/drivers/scsi/aic7xxx.c Tue Dec 8 02:06:02 1998 @@ -354,7 +354,7 @@ 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.1.4" +#define AIC7XXX_C_VERSION "5.1.5" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -579,7 +579,8 @@ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AHA-294X Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AIC-7896/7 Ultra2 SCSI host adapter", /* AIC_7896 */ - "Adaptec AHA-394X Ultra2 SCSI host adapter" /* AIC_7897 */ + "Adaptec AHA-394X Ultra2 SCSI host adapter", /* AIC_7897 */ + "Adaptec PCMCIA SCSI controller", /* card bus stuff */ }; /* @@ -941,7 +942,6 @@ struct aic7xxx_scb *q_next; /* next scb in queue */ volatile scb_flag_type flags; /* current state of scb */ struct hw_scatterlist *sg_list; /* SG list in adapter format */ - void *kmalloc_ptr; unsigned char tag_action; unsigned char sg_count; unsigned char sense_cmd[6]; /* @@ -952,6 +952,7 @@ * don't have to calculate anything * during underflow/overflow/stat code */ + void *kmalloc_ptr; }; /* @@ -1029,42 +1030,39 @@ * This is the first 64 bytes in the host struct */ - struct Scsi_Host *host; /* pointer to scsi host */ - struct aic7xxx_host *next; /* allow for multiple IRQs */ - int host_no; /* SCSI host number */ - unsigned long base; /* card base address */ - volatile unsigned char *maddr; /* memory mapped address */ - unsigned long mbase; /* I/O memory address */ + /* + * We are grouping things here....first, items that get either read or + * written with nearly every interrupt + */ volatile ahc_flag_type flags; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) - spinlock_t spin_lock; -#endif - volatile unsigned char cpu_lock_count[NR_CPUS]; - ahc_chip chip; /* chip type */ ahc_feature features; /* chip features */ - unsigned long last_reset; + unsigned long base; /* card base address */ + volatile unsigned char *maddr; /* memory mapped address */ unsigned long isr_count; /* Interrupt count */ unsigned long spurious_int; - struct target_cmd *targetcmds; - unsigned int num_targetcmds; + scb_data_type *scb_data; + struct aic7xxx_cmd_queue { + Scsi_Cmnd *head; + Scsi_Cmnd *tail; + } completeq; + + /* + * Things read/written on nearly every entry into aic7xxx_queue() + */ + volatile scb_queue_type waiting_scbs; unsigned short discenable; /* Targets allowed to disconnect */ unsigned short tagenable; /* Targets using tagged I/O */ unsigned short orderedtag; /* Ordered Q tags allowed */ - volatile unsigned char activescbs; /* active scbs */ - volatile unsigned char max_activescbs; unsigned char unpause; /* unpause value for HCNTRL */ unsigned char pause; /* pause value for HCNTRL */ volatile unsigned char qoutfifonext; + volatile unsigned char activescbs; /* active scbs */ + volatile unsigned char max_activescbs; volatile unsigned char qinfifonext; - /* - * MAX_TARGETS is currently == 16, so that makes these entries the next - * 64 bytes - */ - #define DEVICE_PRESENT 0x01 #define BUS_DEVICE_RESET_PENDING 0x02 -#define DEVICE_TIMEOUT 0x04 +#define DEVICE_RESET_DELAY 0x04 #define DEVICE_PRINT_SDTR 0x08 #define DEVICE_PRINT_WDTR 0x10 #define DEVICE_SUCCESS 0x20 @@ -1075,38 +1073,26 @@ volatile unsigned char dev_temp_queue_depth[MAX_TARGETS]; unsigned char dev_commands_sent[MAX_TARGETS]; - /* - * The next 128 (or 256 on 64 bit machines).... - */ - Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS]; - Scsi_Cmnd *dev_sdtr_cmnd[MAX_TARGETS]; - - /* - * The next 64.... - */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) + spinlock_t spin_lock; + volatile unsigned char cpu_lock_count[NR_CPUS]; +#endif - long dev_last_reset[MAX_TARGETS]; + unsigned short dev_timer_active; /* Which devs have a timer set */ - /* - * The next 64.... - */ +#ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS + Scsi_Cmnd *dev_wdtr_cmnd[MAX_TARGETS]; + Scsi_Cmnd *dev_sdtr_cmnd[MAX_TARGETS]; +#endif - unsigned char dev_mid_level_queue_depth[MAX_TARGETS]; unsigned char dev_last_queue_full[MAX_TARGETS]; unsigned char dev_last_queue_full_count[MAX_TARGETS]; unsigned char dev_max_queue_depth[MAX_TARGETS]; - /* - * The next 128.... - */ - volatile scb_queue_type delayed_scbs[MAX_TARGETS]; - /* - * - */ - - struct timer_list dev_timer[MAX_TARGETS]; + unsigned long dev_expires[MAX_TARGETS]; + struct timer_list dev_timer; /* * The next 64.... @@ -1120,16 +1106,6 @@ unsigned char msg_len; /* Length of message */ unsigned char msg_index; /* Index into msg_buf array */ transinfo_type transinfo[MAX_TARGETS]; - volatile scb_queue_type waiting_scbs; /* - * SCBs waiting for space in - * the QINFIFO. - */ - scb_data_type *scb_data; - - struct aic7xxx_cmd_queue { - Scsi_Cmnd *head; - Scsi_Cmnd *tail; - } completeq; /* @@ -1170,6 +1146,12 @@ struct seeprom_config sc; unsigned short sc_type; unsigned short sc_size; + struct aic7xxx_host *next; /* allow for multiple IRQs */ + struct Scsi_Host *host; /* pointer to scsi host */ + int host_no; /* SCSI host number */ + unsigned long mbase; /* I/O memory address */ + unsigned long last_reset; + ahc_chip chip; /* chip type */ /* * Statistics Kept: @@ -1189,16 +1171,22 @@ * proc stats code is enabled. */ struct aic7xxx_xferstats { - long xfers; /* total xfer count */ long w_total; /* total writes */ - long w_total512; /* 512 byte blocks written */ long r_total; /* total reads */ - long r_total512; /* 512 byte blocks read */ #ifdef AIC7XXX_PROC_STATS + long xfers; /* total xfer count */ + long w_total512; /* 512 byte blocks written */ + long r_total512; /* 512 byte blocks read */ long w_bins[10]; /* binned write */ long r_bins[10]; /* binned reads */ #endif /* AIC7XXX_PROC_STATS */ } stats[MAX_TARGETS][MAX_LUNS]; /* [(channel << 3)|target][lun] */ + +#if 0 + struct target_cmd *targetcmds; + unsigned int num_targetcmds; +#endif + }; /* @@ -1282,11 +1270,9 @@ */ static int aic7xxx_reverse_scan = 0; /* - * This setting enables a hack to fix the IRQ settings on buggy 7895 - * MB controller setups: - * -1 == Disable this hack - * 0 == Use the Channel A IRQ for both channels - * 1 == Use the Channel B IRQ for both channels + * Should we force EXTENDED translation on a controller. + * 0 == Use whatever is in the SEEPROM or default to off + * 1 == Use whatever is in the SEEPROM or default to on */ static unsigned int aic7xxx_extended = 0; /* @@ -1511,6 +1497,21 @@ for(i=0; i= 0L); +} + +static inline int +timer_pending(struct timer_list *timer) +{ + return( timer->prev != NULL ); +} + +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 + #endif static inline unsigned char @@ -1950,7 +1951,10 @@ { printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no); } +#if 0 download_consts[TMODE_NUMCMDS] = p->num_targetcmds; +#endif + download_consts[TMODE_NUMCMDS] = 0; cur_patch = &sequencer_patches[0]; downloaded = 0; skip_addr = 0; @@ -3077,11 +3081,6 @@ #endif /* AIC7XXX_PROC_STATS */ sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7]; - sp->xfers++; -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if ( (sp->xfers > 16) && (aic7xxx_verbose > 0xffff) ) - aic7xxx_verbose &= 0xffff; -#endif /* * For block devices, cmd->request.cmd is always == either READ or @@ -3093,16 +3092,26 @@ (cmd->data_cmnd[0] == WRITE_FILEMARKS) ) { sp->w_total++; - sp->w_total512 += (actual >> 9); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) ) + aic7xxx_verbose &= 0xffff; +#endif #ifdef AIC7XXX_PROC_STATS + sp->xfers++; + sp->w_total512 += (actual >> 9); ptr = sp->w_bins; #endif /* AIC7XXX_PROC_STATS */ } else { sp->r_total++; - sp->r_total512 += (actual >> 9); +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) ) + aic7xxx_verbose &= 0xffff; +#endif #ifdef AIC7XXX_PROC_STATS + sp->xfers++; + sp->r_total512 += (actual >> 9); ptr = sp->r_bins; #endif /* AIC7XXX_PROC_STATS */ } @@ -3401,6 +3410,10 @@ for (i = min_target; i <= max_target; i++) { + if ( i == p->scsi_id ) + { + continue; + } if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS)) printk(INFO_LEAD "Cleaning up status information " "and delayed_scbs.\n", p->host_no, channel, i, lun); @@ -3420,23 +3433,14 @@ p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING; if ( tag == SCB_LIST_NULL ) { - p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR; - p->dev_last_reset[i] = jiffies; + p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR | + DEVICE_RESET_DELAY; + p->dev_expires[i] = jiffies + (4 * HZ); + p->dev_timer_active |= (0x01 << i); p->dev_last_queue_full_count[i] = 0; p->dev_last_queue_full[i] = 0; p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i]; - /* - * In case this isn't a full bus reset, we want to add a 4 second timer in - * here so that we can delay all re-sent commands for this device for the - * 4 seconds and then have our timer routine pick them back up. - */ - if(p->dev_timer[i].expires) - { - del_timer(&p->dev_timer[i]); - } - p->dev_timer[i].expires = jiffies + (4 * HZ); - add_timer(&p->dev_timer[i]); } for(j=0; jhost_no, channel, i, lun); scbq_init(&p->delayed_scbs[i]); } - if ( (p->delayed_scbs[i].head == NULL) && - (p->dev_timer[i].expires) ) + if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) || + time_after_eq(p->dev_timer.expires, p->dev_expires[i]) ) { - del_timer(&p->dev_timer[i]); - p->dev_timer[i].expires = 0; + del_timer(&p->dev_timer); + p->dev_timer.expires = p->dev_expires[i]; + add_timer(&p->dev_timer); + p->dev_timer_active |= (0x01 << p->scsi_id); } } } @@ -4000,38 +4006,17 @@ tindex = TARGET_INDEX(scb->cmd); if ( !scb->tag_action && (p->tagenable & (1< 0xffff) - printk(INFO_LEAD "Reducing Queue depth for untagged command.\n", - p->host_no, CTL_OF_SCB(scb)); -#endif p->dev_temp_queue_depth[tindex] = 1; } if ( (p->dev_active_cmds[tindex] >= p->dev_temp_queue_depth[tindex]) || - (p->dev_last_reset[tindex] >= (jiffies - (4 * HZ))) ) + (p->dev_flags[tindex] & DEVICE_RESET_DELAY) ) { -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if (aic7xxx_verbose > 0xffff) - printk(INFO_LEAD "Moving SCB to Delayed Queue.\n", - p->host_no, CTL_OF_SCB(scb)); -#endif scbq_insert_tail(&p->delayed_scbs[tindex], scb); - if ( !(p->dev_timer[tindex].expires) && - !(p->dev_active_cmds[tindex]) ) - { - p->dev_timer[tindex].expires = p->dev_last_reset[tindex] + (4 * HZ); - add_timer(&p->dev_timer[tindex]); - } } else { scb->flags &= ~SCB_WAITINGQ; -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if (aic7xxx_verbose > 0xffff) - printk(INFO_LEAD "Sending command %d/0x%x to QINFIFO\n", p->host_no, - CTL_OF_SCB(scb), scb->hscb->tag, scb->flags); -#endif p->dev_active_cmds[tindex]++; p->activescbs++; if ( !(scb->tag_action) ) @@ -4044,16 +4029,6 @@ } if (sent) { -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if (aic7xxx_verbose > 0xffff) - { - printk(INFO_LEAD "Sending commands to QINFIFO\n", p->host_no, - -1, -1, -1); - if ( (p->isr_count < 16) && (aic7xxx_panic_on_abort) && - (p->flags & AHC_PAGESCBS) ) - aic7xxx_check_scbs(p, "While sending commands to QINFIFO"); - } -#endif if (p->features & AHC_QUEUE_REGS) aic_outb(p, p->qinfifonext, HNSCB_QOFF); else @@ -4154,17 +4129,18 @@ #else spin_lock_irqsave(&io_request_lock, cpu_flags); #endif + p->dev_timer_active &= ~(0x01 << p->scsi_id); for(i=0; idev_timer[i].expires) && - (p->dev_timer[i].expires <= jiffies) ) + if ( i == p->scsi_id ) { - p->dev_timer[i].expires = 0; - if ( (p->dev_timer[i].prev != NULL) || - (p->dev_timer[i].next != NULL) ) - { - del_timer(&p->dev_timer[i]); - } + continue; + } + if ( (p->dev_timer_active & (0x01 << i)) && + time_after_eq(jiffies, p->dev_expires[i]) ) + { + p->dev_timer_active &= ~(0x01 << i); + p->dev_flags[i] &= ~DEVICE_RESET_DELAY; p->dev_temp_queue_depth[i] = p->dev_max_queue_depth[i]; j = 0; while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) && @@ -4184,7 +4160,27 @@ */ } } + else if ( p->dev_timer_active & (0x01 << i) ) + { + if ( p->dev_timer_active & (0x01 << p->scsi_id) ) + { + if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) ) + { + p->dev_timer.expires = p->dev_expires[i]; + } + } + else + { + p->dev_timer.expires = p->dev_expires[i]; + p->dev_timer_active |= (0x01 << p->scsi_id); + } + } } + if ( p->dev_timer_active & (0x01 << p->scsi_id) ) + { + add_timer(&p->dev_timer); + } + aic7xxx_run_waiting_queues(p); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) DRIVER_UNLOCK @@ -4897,17 +4893,29 @@ p->activescbs--; scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY; - if (p->dev_timer[tindex].expires == 0) + if ( !(p->dev_timer_active & (0x01 << tindex)) ) { + p->dev_timer_active |= (0x01 << tindex); if ( p->dev_active_cmds[tindex] ) { - p->dev_timer[tindex].expires = jiffies + (HZ * 2); - add_timer(&p->dev_timer[tindex]); + p->dev_expires[tindex] = jiffies + (HZ * 2); } else { - p->dev_timer[tindex].expires = jiffies + (HZ / 2); - add_timer(&p->dev_timer[tindex]); + p->dev_expires[tindex] = jiffies + (HZ / 2); + } + if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) ) + { + p->dev_timer.expires = p->dev_expires[tindex]; + p->dev_timer_active |= (0x01 << p->scsi_id); + add_timer(&p->dev_timer); + } + else if ( time_after_eq(p->dev_timer.expires, + p->dev_expires[tindex]) ) + { + del_timer(&p->dev_timer); + p->dev_timer.expires = p->dev_expires[tindex]; + add_timer(&p->dev_timer); } } #ifdef AIC7XXX_VERBOSE_DEBUGGING @@ -5267,6 +5275,35 @@ { maxsync = AHC_SYNCRATE_FAST; } + /* + * We might have a device that is starting negotiation with us + * before we can start up negotiation with it....be prepared to + * have a device ask for a higher speed then we want to give it + * in that case + */ + if (!(p->sdtr_pending & target_mask)) + { + if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) + { + /* + * Not only is the device starting this up, but it also hasn't + * been scanned yet, so this would likely be our TUR or our + * INQUIRY command at scan time, so we need to use the + * settings from the SEEPROM if they existed. Of course, even + * if we didn't find a SEEPROM, we stuffed default values into + * the user settings anyway, so use those in all cases. + */ + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->needsdtr_copy |= target_mask; + } + if ( p->transinfo[tindex].goal_period > period ) + period = p->transinfo[tindex].goal_period; + if ( p->transinfo[tindex].goal_offset < offset ) + saved_offset = offset = p->transinfo[tindex].goal_offset; + } #ifdef AIC7XXX_VERBOSE_DEBUGGING if (aic7xxx_verbose > 0xffff) @@ -5309,7 +5346,6 @@ */ aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset, AHC_TRANS_GOAL|AHC_TRANS_QUITE); - p->needsdtr_copy &= ~target_mask; } /* * Did we start this, if not, or if we went to low and had to @@ -5325,32 +5361,6 @@ } else { - /* - * Send a reply SDTR back. Even if we sent the first one, it - * is valid to send another one out immediately to re-negotiate - * things, and a few devices don't like getting rejects after - * we already sent them one SDTR. Just send an SDTR for async - * this time if need be (or for the correct params if we didn't - * start all of this). If this is a Reject Reply type message, - * then we've put the async settings into the goal area for - * future reference (when we get the AWAITING_MSG interrupt). - * If this is a case where we are responding to the target's - * initiated SDTR, then leave our own goal and user values in - * place (unless the device hasn't been scanned yet, in which - * case, put the user values into the goal values so we don't - * send out an Async message). - */ - if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) ) - { - p->transinfo[tindex].goal_width = - p->transinfo[tindex].user_width; - p->transinfo[tindex].goal_period = - p->transinfo[tindex].user_period; - p->transinfo[tindex].goal_offset = - p->transinfo[tindex].user_offset; - p->needwdtr_copy |= target_mask; - p->needsdtr_copy |= target_mask; - } scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_SDTR; aic_outb(p, HOST_MSG, MSG_OUT); @@ -5419,14 +5429,15 @@ * it contacted us first, mark it as such and copy the user stuff * over to the goal stuff. */ - p->transinfo[tindex].goal_width = - p->transinfo[tindex].user_width; p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; p->transinfo[tindex].goal_offset = p->transinfo[tindex].user_offset; + p->transinfo[tindex].goal_width = + p->transinfo[tindex].user_width; p->needwdtr_copy |= target_mask; p->needsdtr_copy |= target_mask; + p->dev_flags[tindex] |= DEVICE_SCANNED; } switch(bus_width) { @@ -5444,6 +5455,8 @@ { p->needwdtr_copy &= ~target_mask; bus_width = MSG_EXT_WDTR_BUS_8_BIT; + aic7xxx_set_width(p, target, channel, lun, bus_width, + AHC_TRANS_GOAL|AHC_TRANS_QUITE); break; } } @@ -6404,7 +6417,6 @@ target_mask = (1 << tindex); device->queue_depth = default_depth; - p->dev_mid_level_queue_depth[tindex] = 3; p->dev_temp_queue_depth[tindex] = 1; p->dev_max_queue_depth[tindex] = 1; p->tagenable &= ~target_mask; @@ -6469,7 +6481,6 @@ } p->dev_max_queue_depth[tindex] = device->queue_depth; p->dev_temp_queue_depth[tindex] = device->queue_depth; - p->dev_mid_level_queue_depth[tindex] = device->queue_depth; p->tagenable |= target_mask; p->orderedtag |= target_mask; device->tagged_queue = 1; @@ -7464,7 +7475,7 @@ } p->host = host; - p->last_reset = 0; + p->last_reset = jiffies; p->host_no = host->host_no; host->unique_id = p->instance; p->isr_count = 0; @@ -7473,6 +7484,10 @@ p->completeq.tail = NULL; scbq_init(&p->scb_data->free_scbs); scbq_init(&p->waiting_scbs); + init_timer(&p->dev_timer); + p->dev_timer.data = (unsigned long)p; + p->dev_timer.function = (void *)aic7xxx_timer; + p->dev_timer_active = 0; for (i = 0; i < NUMBER(p->untagged_scbs); i++) { @@ -7491,17 +7506,12 @@ p->dev_commands_sent[i] = 0; p->dev_flags[i] = 0; p->dev_active_cmds[i] = 0; - p->dev_last_reset[i] = 0; p->dev_last_queue_full[i] = 0; p->dev_last_queue_full_count[i] = 0; p->dev_max_queue_depth[i] = 1; p->dev_temp_queue_depth[i] = 1; - p->dev_mid_level_queue_depth[i] = 3; + p->dev_expires[i] = 0; scbq_init(&p->delayed_scbs[i]); - init_timer(&p->dev_timer[i]); - p->dev_timer[i].expires = 0; - p->dev_timer[i].data = (unsigned long)p; - p->dev_timer[i].function = (void *)aic7xxx_timer; } printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no, @@ -8169,6 +8179,21 @@ have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), scarray, p->sc_size, C46); } + if (!have_seeprom) + { + p->sc_size = 128; + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, p->sc_type); + if (!have_seeprom) + { + if(p->sc_type == C46) + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C56_66); + else + have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)), + scarray, p->sc_size, C46); + } + } break; } @@ -8200,7 +8225,8 @@ sc->adapter_control &= ~CFAUTOTERM; sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM; } - p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; + if (aic7xxx_extended) + p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B; } else { @@ -8354,7 +8380,8 @@ } else if (sc->device_flags[i] & CFNEWULTRAFORMAT) { - if ( (sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03 ) + if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) && + !(p->features & AHC_ULTRA2) ) { sc->device_flags[i] &= ~CFXFER; sc->device_flags[i] |= CFSYNCHISULTRA; @@ -8635,12 +8662,6 @@ current_p = current_p->next; current_p->next = temp_p; } - if (aic7xxx_extended) - { - temp_p->flags |= AHC_EXTEND_TRANS_A; - if (temp_p->flags & AHC_MULTI_CHANNEL) - temp_p->flags |= AHC_EXTEND_TRANS_B; - } switch (type) { @@ -8859,6 +8880,10 @@ AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, AHC_AIC7896_FE, 23, 32, C56_66 }, + {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_AIC7860_FE, 24, + 32, C46 }, }; unsigned short command; @@ -9340,9 +9365,6 @@ aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS); } - if (aic7xxx_extended) - temp_p->flags |= AHC_EXTEND_TRANS_A; - if ( list_p == NULL ) { list_p = current_p = temp_p; @@ -10240,7 +10262,7 @@ { mask = (0x01 << i); printk(INFO_LEAD "dev_flags=0x%x, WDTR:%c/%c/%c, SDTR:%c/%c/%c," - " q_depth=%d:%d:%d\n", + " q_depth=%d:%d\n", p->host_no, 0, i, 0, p->dev_flags[i], (p->wdtr_pending & mask) ? 'Y' : 'N', (p->needwdtr & mask) ? 'Y' : 'N', @@ -10249,7 +10271,7 @@ (p->needsdtr & mask) ? 'Y' : 'N', (p->needsdtr_copy & mask) ? 'Y' : 'N', p->dev_active_cmds[i], - p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]); + p->dev_max_queue_depth[i] ); printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0, aic_inb(p, TARG_SCSIRATE + i)); if (p->features & AHC_ULTRA2) @@ -10890,7 +10912,7 @@ { action = HOST_RESET; } - if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) && + if ( (p->dev_flags[tindex] & DEVICE_RESET_DELAY) && !(action & (HOST_RESET | BUS_RESET))) { if (aic7xxx_verbose & VERBOSE_RESET_PROCESS) @@ -10973,9 +10995,25 @@ p->msg_len = 0; } aic7xxx_run_done_queue(p, TRUE); + /* + * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is + * in need of being re-started, so send it on through to aic7xxx_queue + * and let it set until the delay is over. This keeps it from dying + * entirely and avoids getting a bogus dead command back through the + * mid-level code due to too many retries. + */ + if ( flags & SCSI_RESET_SYNCHRONOUS ) + { + cmd->result = DID_RESET << 16; + cmd->done(cmd); + } p->flags &= ~AHC_IN_RESET; - /* We can't rely on run_waiting_queues to unpause the sequencer for - * PCI based controllers since we use AAP */ + /* + * We can't rely on run_waiting_queues to unpause the sequencer for + * PCI based controllers since we use AAP. NOTE: this also sets + * the timer for the one command we might have queued in the case + * of a synch reset. + */ aic7xxx_run_waiting_queues(p); unpause_sequencer(p, FALSE); DRIVER_UNLOCK diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c --- linux.vanilla/drivers/scsi/aic7xxx_proc.c Sun Dec 6 00:14:43 1998 +++ linux/drivers/scsi/aic7xxx_proc.c Tue Dec 8 02:31:14 1998 @@ -132,7 +132,7 @@ { for (lun = 0; lun < MAX_LUNS; lun++) { - if (p->stats[target][lun].xfers != 0) + if (p->stats[target][lun].r_total != 0) #ifdef AIC7XXX_PROC_STATS size += 512; #else @@ -278,7 +278,7 @@ for (lun = 0; lun < MAX_LUNS; lun++) { sp = &p->stats[target][lun]; - if (sp->xfers == 0) + if (sp->r_total == 0) { continue; } @@ -331,11 +331,11 @@ p->transinfo[target].cur_period, p->transinfo[target].cur_offset, p->transinfo[target].cur_width); +#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, " Total transfers %ld (%ld read;%ld written)\n", sp->xfers, sp->r_total, sp->w_total); size += sprintf(BLS, " blks(512) rd=%ld; blks(512) wr=%ld\n", sp->r_total512, sp->w_total512); -#ifdef AIC7XXX_PROC_STATS size += sprintf(BLS, "%s\n", HDRB); size += sprintf(BLS, " Reads:"); for (i = 0; i < NUMBER(sp->r_bins); i++) @@ -348,6 +348,9 @@ { size += sprintf(BLS, "%6ld ", sp->w_bins[i]); } +#else + size += sprintf(BLS, " Total transfers: %ld/%ld read/written)\n", + sp->r_total, sp->w_total); #endif /* AIC7XXX_PROC_STATS */ size += sprintf(BLS, "\n\n"); } diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/atp870u.c linux/drivers/scsi/atp870u.c --- linux.vanilla/drivers/scsi/atp870u.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/atp870u.c Fri Dec 11 17:06:49 1998 @@ -0,0 +1,2029 @@ +/* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $ + * linux/kernel/atp870u.c + * + * Copyright (C) 1997 Wu Ching Chen + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include "hosts.h" + + +#include "atp870u.h" +#include /* for CONFIG_PCI */ + +#include + +struct proc_dir_entry proc_scsi_atp870u = { + PROC_SCSI_ATP870U, 7, "atp870u", + S_IFDIR | S_IRUGO | S_IXUGO, 2 +}; + +void mydlyu(unsigned int); +/* +static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $"; +*/ + +static unsigned char admaxu=1,host_idu[2],chip_veru[2],scam_on[2],global_map[2]; +static unsigned short int active_idu[2],wide_idu[2],sync_idu,ultra_map[2]; +static int workingu[2]={0,0}; +static Scsi_Cmnd *querequ[2][qcnt],*curr_req[2][16]; +static unsigned char devspu[2][16] = {{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}, + {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}}; +static unsigned char dirctu[2][16],last_cmd[2],in_snd[2],in_int[2]; +static unsigned char ata_cdbu[2][16]; +static unsigned int ioportu[2]={0,0}; +static unsigned int irqnumu[2]={0,0}; +static unsigned short int pciportu[2]; +static unsigned long prdaddru[2][16],tran_lenu[2][16],last_lenu[2][16]; +static unsigned char prd_tableu[2][16][1024]; +static unsigned char *prd_posu[2][16]; +static unsigned char quhdu[2],quendu[2]; +static unsigned char devtypeu[2][16] = {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; +static struct Scsi_Host * atp_host[2]={NULL,NULL}; + +static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short int tmpcip,id; + unsigned char i,j,h,tarid,lun; + unsigned char *prd; + Scsi_Cmnd *workrequ; + unsigned int workportu,tmport; + unsigned long adrcntu,k; + int errstus; + + for ( h=0; h < 2; h++ ) + { + if ( ( irq & 0x0f ) == irqnumu[h] ) + { + goto irq_numok; + } + } + return; +irq_numok: + in_int[h]=1; + workportu=ioportu[h]; + tmport=workportu; + + if ( workingu[h] != 0 ) + { + tmport += 0x1f; + j=inb(tmport); + tmpcip=pciportu[h]; + if ((inb(tmpcip) & 0x08) != 0) + { + tmpcip += 0x2; + while((inb(tmpcip) & 0x08) != 0); + } + tmpcip=pciportu[h]; + outb(0x00,tmpcip); + tmport -=0x08; + i=inb(tmport); + if ((j & 0x40) == 0) + { + if ((last_cmd[h] & 0x40) == 0) + { + last_cmd[h]=0xff; + } + } + else + { + last_cmd[h] |= 0x40; + } + tmport -= 0x02; + tarid=inb(tmport); + tmport += 0x02; + if ((tarid & 0x40) != 0) + { + tarid=(tarid & 0x07) | 0x08; + } + else + { + tarid &= 0x07; + } + if ( i == 0x85 ) + { + if (wide_idu[h] != 0) + { + tmport=workportu+0x1b; + j=inb(tmport) & 0x0e; + j |= 0x01; + outb(j,tmport); + } + if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) && + (in_snd[h] == 0)) + { + send_s870(h); + } + in_int[h]=0; + return; + } + if ( i == 0x21 ) + { + tmport -= 0x05; + adrcntu=0; + ((unsigned char *)&adrcntu)[2]=inb(tmport++); + ((unsigned char *)&adrcntu)[1]=inb(tmport++); + ((unsigned char *)&adrcntu)[0]=inb(tmport); + k=last_lenu[h][tarid]; + k -= adrcntu; + tran_lenu[h][tarid]= k; + last_lenu[h][tarid]=adrcntu; + tmport -= 0x04; + outb(0x41,tmport); + tmport += 0x08; + outb(0x08,tmport); + in_int[h]=0; + return ; + } + + if ((i == 0x80) || (i == 0x8f)) + { + lun=0; + tmport -= 0x07; + j=inb(tmport); + if ( j == 0x44 ) + { + tmport += 0x0d; + lun=inb(tmport) & 0x07; + } + else + { + if ( j == 0x41 ) + { + tmport += 0x02; + adrcntu=0; + ((unsigned char *)&adrcntu)[2]=inb(tmport++); + ((unsigned char *)&adrcntu)[1]=inb(tmport++); + ((unsigned char *)&adrcntu)[0]=inb(tmport); + k=last_lenu[h][tarid]; + k -= adrcntu; + tran_lenu[h][tarid]= k; + last_lenu[h][tarid]=adrcntu; + tmport += 0x04; + outb(0x08,tmport); + in_int[h]=0; + return ; + } + else + { + outb(0x46,tmport); + dirctu[h][tarid]=0x00; + tmport += 0x02; + outb(0x00,tmport++); + outb(0x00,tmport++); + outb(0x00,tmport++); + tmport+=0x03; + outb(0x08,tmport); + in_int[h]=0; + return; + } + } + tmport=workportu + 0x10; + outb(0x45,tmport); + tmport += 0x06; + tarid=inb(tmport); + if ((tarid & 0x10) != 0) + { + tarid=(tarid & 0x07) | 0x08; + } + else + { + tarid &= 0x07; + } + workrequ=curr_req[h][tarid]; + tmport=workportu + 0x0f; + outb(lun,tmport); + tmport += 0x02; + outb(devspu[h][tarid],tmport++); + adrcntu=tran_lenu[h][tarid]; + k=last_lenu[h][tarid]; + outb(((unsigned char *)&k)[2],tmport++); + outb(((unsigned char *)&k)[1],tmport++); + outb(((unsigned char *)&k)[0],tmport++); + j=tarid; + if ( tarid > 7 ) + { + j = (j & 0x07) | 0x40; + } + j |= dirctu[h][tarid]; + outb(j,tmport++); + outb(0x80,tmport); + tmport=workportu + 0x1b; + j=inb(tmport) & 0x0e; + id=1; + id=id << tarid; + if ((id & wide_idu[h]) != 0) + { + j |= 0x01; + } + outb(j,tmport); + if ( last_lenu[h][tarid] == 0 ) + { + tmport=workportu + 0x18; + outb(0x08,tmport); + in_int[h]=0; + return ; + } + prd=prd_posu[h][tarid]; + while ( adrcntu != 0 ) + { + id=((unsigned short int *)(prd))[2]; + if ( id == 0 ) + { + k=0x10000; + } + else + { + k=id; + } + if ( k > adrcntu ) + { + ((unsigned short int *)(prd))[2] =(unsigned short int) + (k - adrcntu); + ((unsigned long *)(prd))[0] += adrcntu; + adrcntu=0; + prd_posu[h][tarid]=prd; + } + else + { + adrcntu -= k; + prdaddru[h][tarid] += 0x08; + prd += 0x08; + if ( adrcntu == 0 ) + { + prd_posu[h][tarid]=prd; + } + } + } + tmpcip=pciportu[h] + 0x04; + outl(prdaddru[h][tarid],tmpcip); + tmpcip -= 0x02; + outb(0x06,tmpcip); + outb(0x00,tmpcip); + tmpcip -= 0x02; + tmport=workportu + 0x18; + if ( dirctu[h][tarid] != 0 ) + { + outb(0x08,tmport); + outb(0x01,tmpcip); + in_int[h]=0; + return; + } + outb(0x08,tmport); + outb(0x09,tmpcip); + in_int[h]=0; + return; + } + + workrequ=curr_req[h][tarid]; + if ( i == 0x42 ) + { + errstus=0x02; + workrequ->result=errstus; + goto go_42; + } + if ( i == 0x16 ) + { + errstus=0; + tmport -= 0x08; + errstus=inb(tmport); + workrequ->result=errstus; +/* if ( errstus == 0x02 ) + { + tmport +=0x10; + if ((inb(tmport) & 0x80) != 0) + { + printk(" autosense "); + } + tmport -=0x09; + outb(0,tmport); + tmport=workportu+0x3a; + outb((unsigned char)(inb(tmport) | 0x10),tmport); + tmport -= 0x39; + + outb(0x08,tmport++); + outb(0x7f,tmport++); + outb(0x03,tmport++); + outb(0x00,tmport++); + outb(0x00,tmport++); + outb(0x00,tmport++); + outb(0x0e,tmport++); + outb(0x00,tmport); + tmport+=0x07; + outb(0x00,tmport++); + tmport++; + outb(devspu[h][workrequ->target],tmport++); + outb(0x00,tmport++); + outb(0x00,tmport++); + outb(0x0e,tmport++); + tmport+=0x03; + outb(0x09,tmport); + tmport+=0x07; + i=0; + adrcntu=(unsigned long)(&workrequ->sense_buffer[0]); +get_sens: + j=inb(tmport); + if ((j & 0x01) != 0) + { + tmport-=0x06; + (unsigned char)(((caddr_t) adrcntu)[i++])=inb(tmport); + tmport+=0x06; + goto get_sens; + } + if ((j & 0x80) == 0) + { + goto get_sens; + } + if ((j & 0x40) == 0) + { + tmport-=0x08; + i=inb(tmport); + } + tmport=workportu+0x3a; + outb((unsigned char)(inb(tmport) & 0xef),tmport); + tmport=workportu+0x01; + outb(0x2c,tmport); + tmport += 0x15; + outb(0x80,tmport); + } */ +go_42: + (*workrequ->scsi_done)(workrequ); + curr_req[h][tarid]=0; + workingu[h]--; + if (wide_idu[h] != 0) + { + tmport=workportu+0x1b; + j=inb(tmport) & 0x0e; + j |= 0x01; + outb(j,tmport); + } + if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) && + (in_snd[h] == 0)) + { + send_s870(h); + } + in_int[h]=0; + return; + } + if ( i == 0x4f ) + { + i=0x89; + } + i &= 0x0f; + if ( i == 0x09 ) + { + tmpcip=tmpcip+4; + outl(prdaddru[h][tarid],tmpcip); + tmpcip=tmpcip-2; + outb(0x06,tmpcip); + outb(0x00,tmpcip); + tmpcip=tmpcip-2; + tmport=workportu+0x10; + outb(0x41,tmport); + dirctu[h][tarid]=0x00; + tmport += 0x08; + outb(0x08,tmport); + outb(0x09,tmpcip); + in_int[h]=0; + return; + } + if ( i == 0x08 ) + { + tmpcip=tmpcip+4; + outl(prdaddru[h][tarid],tmpcip); + tmpcip=tmpcip-2; + outb(0x06,tmpcip); + outb(0x00,tmpcip); + tmpcip=tmpcip-2; + tmport=workportu+0x10; + outb(0x41,tmport); + tmport += 0x05; + outb((unsigned char)(inb(tmport) | 0x20),tmport); + dirctu[h][tarid]=0x20; + tmport += 0x03; + outb(0x08,tmport); + outb(0x01,tmpcip); + in_int[h]=0; + return; + } + tmport -= 0x07; + if ( i == 0x0a ) + { + outb(0x30,tmport); + } + else + { + outb(0x46,tmport); + } + dirctu[h][tarid]=0x00; + tmport += 0x02; + outb(0x00,tmport++); + outb(0x00,tmport++); + outb(0x00,tmport++); + tmport+=0x03; + outb(0x08,tmport); + in_int[h]=0; + return; + } + else + { + tmport=workportu+0x17; + inb(tmport); + workingu[h]=0; + in_int[h]=0; + return; + } +} + +int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done)(Scsi_Cmnd *)) +{ + unsigned char i,h; + unsigned long flags; + unsigned short int m; + unsigned int tmport; + + for( h=0; h <= admaxu; h++ ) + { + if ( req_p->host == atp_host[h] ) + { + goto host_ok; + } + } + return 0; +host_ok: + if ( req_p->channel != 0 ) + { + req_p->result = 0x00040000; + done(req_p); + return 0; + } + m=1; + m= m << req_p->target; + if ( ( m & active_idu[h] ) == 0 ) + { + req_p->result = 0x00040000; + done(req_p); + return 0; + } + if (done) + { + req_p->scsi_done = done; + } + else + { + printk("atp870u_queuecommand: done can't be NULL\n"); + req_p->result = 0; + done(req_p); + return 0; + } + quendu[h]++; + if ( quendu[h] >= qcnt ) + { + quendu[h]=0; + } + wait_que_empty: + if ( quhdu[h] == quendu[h] ) + { + goto wait_que_empty; + } + save_flags(flags); + cli(); + querequ[h][quendu[h]]=req_p; + if ( quendu[h] == 0 ) + { + i=qcnt-1; + } + else + { + i=quendu[h]-1; + } + tmport = ioportu[h]+0x1c; + restore_flags(flags); + if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 0)) + { + send_s870(h); + } + return 0; +} + +void mydlyu(unsigned int dlycnt ) +{ + unsigned int i ; + for ( i = 0 ; i < dlycnt ; i++ ) + { + inb(0x80); + } +} + +void send_s870(unsigned char h) +{ + unsigned int tmport; + Scsi_Cmnd *workrequ; + unsigned long flags; + unsigned int i; + unsigned char j,tarid; + unsigned char *prd; + unsigned short int tmpcip,w; + unsigned long l,bttl; + unsigned int workportu; + struct scatterlist * sgpnt; + + save_flags(flags); + cli(); + if ( in_snd[h] != 0 ) + { + restore_flags(flags); + return; + } + in_snd[h]=1; + if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0)) + { + last_cmd[h] &= 0x0f; + workrequ=curr_req[h][last_cmd[h]]; + goto cmd_subp; + } + workingu[h]++; + j=quhdu[h]; + quhdu[h]++; + if ( quhdu[h] >= qcnt ) + { + quhdu[h]=0; + } + workrequ=querequ[h][quhdu[h]]; + if ( curr_req[h][workrequ->target] == 0 ) + { + curr_req[h][workrequ->target]=workrequ; + last_cmd[h]=workrequ->target; + goto cmd_subp; + } + quhdu[h]=j; + workingu[h]--; + in_snd[h]=0; + restore_flags(flags); + return ; +cmd_subp: + workportu=ioportu[h]; + tmport=workportu+0x1f; + if ((inb(tmport) & 0xb0) != 0) + { + goto abortsnd; + } + tmport=workportu+0x1c; + if ( inb(tmport) == 0 ) + { + goto oktosend; + } +abortsnd: + last_cmd[h] |= 0x40; + in_snd[h]=0; + restore_flags(flags); + return; +oktosend: + memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len); + if ( ata_cdbu[h][0] == 0x25 ) + { + if ( workrequ->request_bufflen > 8 ) + { + workrequ->request_bufflen=0x08; + } + } + if ( ata_cdbu[h][0] == 0x12 ) + { + if ( workrequ->request_bufflen > 0x24 ) + { + workrequ->request_bufflen = 0x24; + ata_cdbu[h][4]=0x24; + } + } + + tmport=workportu+0x1b; + j=inb(tmport) & 0x0e; + tarid=workrequ->target; + w=1; + w = w << tarid; + if ((w & wide_idu[h]) != 0) + { + j |= 0x01; + } + outb(j,tmport); + tmport=workportu; + outb(workrequ->cmd_len,tmport++); + outb(0x2c,tmport++); + outb(0xcf,tmport++); + for ( i=0 ; i < workrequ->cmd_len ; i++ ) + { + outb(ata_cdbu[h][i],tmport++); + } + tmport=workportu+0x0f; + outb(0x00,tmport); + tmport+=0x02; + outb(devspu[h][tarid],tmport++); + if (workrequ->use_sg) + { + + l=0; + sgpnt = (struct scatterlist *) workrequ->request_buffer; + for(i=0; iuse_sg; i++) + { + if(sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) + { + panic("Foooooooood fight!"); + } + l += sgpnt[i].length; + } + } + else + { + l=workrequ->request_bufflen; + } + outb((unsigned char)(((unsigned char *)(&l))[2]),tmport++); + outb((unsigned char)(((unsigned char *)(&l))[1]),tmport++); + outb((unsigned char)(((unsigned char *)(&l))[0]),tmport++); + j=tarid; + last_lenu[h][j]=l; + tran_lenu[h][j]=0; + if ((j & 0x08) != 0) + { + j=(j & 0x07) | 0x40; + } + if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || + (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) + { + outb((unsigned char)(j | 0x20),tmport++); + } + else + { + outb(j,tmport++); + } + outb(0x80,tmport); + tmport=workportu + 0x1c; + dirctu[h][tarid]=0; + if ( l == 0 ) + { + if ( inb(tmport) == 0 ) + { + tmport=workportu+0x18; + outb(0x08,tmport); + } + else + { + last_cmd[h] |= 0x40; + } + in_snd[h]=0; + restore_flags(flags); + return; + } + tmpcip=pciportu[h]; + prd=&prd_tableu[h][tarid][0]; + prd_posu[h][tarid]=prd; + if (workrequ->use_sg) + { + sgpnt = (struct scatterlist *) workrequ->request_buffer; + i=0; + for(j=0; juse_sg; j++) + { + (unsigned long)(((unsigned long *)(prd))[i >> 1])=(unsigned long)sgpnt[j].address; + (unsigned short int)(((unsigned short int *)(prd))[i+2])=sgpnt[j].length; + (unsigned short int)(((unsigned short int *)(prd))[i+3])=0; + i +=0x04; + } + (unsigned short int)(((unsigned short int *)(prd))[i-1])=0x8000; + } + else + { + bttl=(unsigned long)workrequ->request_buffer; + l=workrequ->request_bufflen; + i=0; + while ( l > 0x10000 ) + { + (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x0000; + (unsigned short int)(((unsigned short int *)(prd))[i+2])=0x0000; + (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; + l -= 0x10000; + bttl += 0x10000; + i += 0x04; + } + (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x8000; + (unsigned short int)(((unsigned short int *)(prd))[i+2])=l; + (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; + } + tmpcip=tmpcip+4; + prdaddru[h][tarid]=(unsigned long)&prd_tableu[h][tarid][0]; + outl(prdaddru[h][tarid],tmpcip); + tmpcip=tmpcip-2; + outb(0x06,tmpcip); + outb(0x00,tmpcip); + tmpcip=tmpcip-2; + if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || + (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) + { + dirctu[h][tarid]=0x20; + if ( inb(tmport) == 0 ) + { + tmport=workportu+0x18; + outb(0x08,tmport); + outb(0x01,tmpcip); + } + else + { + last_cmd[h] |= 0x40; + } + in_snd[h]=0; + restore_flags(flags); + return; + } + if ( inb(tmport) == 0 ) + { + tmport=workportu+0x18; + outb(0x08,tmport); + outb(0x09,tmpcip); + } + else + { + last_cmd[h] |= 0x40; + } + in_snd[h]=0; + restore_flags(flags); + return; + +} + +static void internal_done(Scsi_Cmnd * SCpnt) +{ + SCpnt->SCp.Status++; +} + +int atp870u_command(Scsi_Cmnd * SCpnt) +{ + + atp870u_queuecommand(SCpnt, internal_done); + + SCpnt->SCp.Status = 0; + while (!SCpnt->SCp.Status) + barrier(); + return SCpnt->result; +} + +unsigned char fun_scam ( unsigned char host,unsigned short int * val ) +{ + unsigned int tmport ; + unsigned short int i,k; + unsigned char j; + + tmport = ioportu[host]+0x1c; + outw(*val,tmport); +FUN_D7: + for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ + { + k=inw(tmport); + j= (unsigned char)(k >> 8); + if ((k & 0x8000) != 0) /* DB7 all release? */ + { + goto FUN_D7; + } + } + *val |= 0x4000; /* assert DB6 */ + outw(*val,tmport); + *val &= 0xdfff; /* assert DB5 */ + outw(*val,tmport); +FUN_D5: + for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ + { + if ((inw(tmport) & 0x2000) != 0) /* DB5 all release? */ + { + goto FUN_D5; + } + } + *val |= 0x8000; /* no DB4-0, assert DB7 */ + *val &= 0xe0ff; + outw(*val,tmport); + *val &= 0xbfff; /* release DB6 */ + outw(*val,tmport); +FUN_D6: + for ( i=0; i < 10; i++ ) /* stable >= bus settle delay(400 ns) */ + { + if ((inw(tmport) & 0x4000) != 0) /* DB6 all release? */ + { + goto FUN_D6; + } + } + + return j; +} + +void tscam( unsigned char host ) +{ + + unsigned int tmport ; + unsigned char i,j,k; + unsigned long n; + unsigned short int m,assignid_map,val; + unsigned char mbuf[33],quintet[2]; + static unsigned char g2q_tab[8]={ 0x38,0x31,0x32,0x2b,0x34,0x2d,0x2e,0x27 }; + + + for ( i=0; i < 0x10; i++ ) + { + mydlyu(0xffff); + } + + tmport = ioportu[host]+1; + outb(0x08,tmport++); + outb(0x7f,tmport); + tmport = ioportu[host]+0x11; + outb(0x20,tmport); + + if ((scam_on[host] & 0x40) == 0) + { + return; + } + + m=1; + m <<= host_idu[host]; + j=16; + if ( chip_veru[host] < 4 ) + { + m |= 0xff00; + j=8; + } + assignid_map=m; + tmport = ioportu[host]+0x02; + outb(0x02,tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */ + outb(0,tmport++); + outb(0,tmport++); + outb(0,tmport++); + outb(0,tmport++); + outb(0,tmport++); + outb(0,tmport++); + + for ( i = 0 ; i < j ; i ++ ) + { + m=1; + m=m< 7 ) + { + k=(i & 0x07) | 0x40; + } + else + { + k=i; + } + outb(k,tmport++); + tmport = ioportu[host]+0x1b; + if ( chip_veru[host] == 4 ) + { + outb((unsigned char)((inb(tmport) & 0x0e) | 0x01),tmport); + } + else + { + outb((unsigned char)(inb(tmport) & 0x0e),tmport); + } +wait_rdyok: + tmport = ioportu[host]+0x18; + outb(0x09,tmport); + tmport += 0x07; + + while ((inb(tmport) & 0x80) == 0x00); + tmport -= 0x08; + k=inb(tmport); + if ( k != 0x16 ) + { + if ((k == 0x85) || (k == 0x42)) + { + continue; + } + tmport = ioportu[host]+0x10; + outb(0x41,tmport); + goto wait_rdyok; + } + assignid_map |= m; + + } + tmport = ioportu[host]+0x02; + outb(0x7f,tmport); + tmport = ioportu[host]+0x1b; + outb(0x02,tmport); + + outb(0,0x80); + + val=0x0080; /* bsy */ + tmport = ioportu[host]+0x1c; + outw(val,tmport); + val |=0x0040; /* sel */ + outw(val,tmport); + val |=0x0004; /* msg */ + outw(val,tmport); + inb(0x80); /* 2 deskew delay(45ns*2=90ns) */ + val &=0x007f; /* no bsy */ + outw(val,tmport); + mydlyu(0xffff); /* recommanded SCAM selection response time */ + mydlyu(0xffff); + val &=0x00fb; /* after 1ms no msg */ + outw(val,tmport); +wait_nomsg: + if ((inb(tmport) & 0x04) != 0) + { + goto wait_nomsg; + } + outb(1,0x80); + mydlyu(100); + for ( n=0; n < 0x30000; n++ ) + { + if ((inb(tmport) & 0x80) != 0) /* bsy ? */ + { + goto wait_io; + } + } + goto TCM_SYNC; +wait_io: + for ( n=0; n < 0x30000; n++ ) + { + if ((inb(tmport) & 0x81) == 0x0081) + { + goto wait_io1; + } + } + goto TCM_SYNC; +wait_io1: + inb(0x80); + val |=0x8003; /* io,cd,db7 */ + outw(val,tmport); + inb(0x80); + val &=0x00bf; /* no sel */ + outw(val,tmport); + outb(2,0x80); +TCM_SYNC: + mydlyu(0x800); + if ((inb(tmport) & 0x80) == 0x00) /* bsy ? */ + { + outw(0,tmport--); + outb(0,tmport); + tmport=ioportu[host] + 0x15; + outb(0,tmport); + tmport += 0x03; + outb(0x09,tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0); + tmport -= 0x08; + inb(tmport); + return; + } + + val &= 0x00ff; /* synchronization */ + val |= 0x3f00; + fun_scam(host,&val); + outb(3,0x80); + val &= 0x00ff; /* isolation */ + val |= 0x2000; + fun_scam(host,&val); + outb(4,0x80); + i=8; + j=0; +TCM_ID: + if ((inw(tmport) & 0x2000) == 0) + { + goto TCM_ID; + } + outb(5,0x80); + val &= 0x00ff; /* get ID_STRING */ + val |= 0x2000; + k=fun_scam(host,&val); + if ((k & 0x03) == 0) + { + goto TCM_5; + } + mbuf[j] <<= 0x01; + mbuf[j] &= 0xfe; + if ((k & 0x02) != 0) + { + mbuf[j] |= 0x01; + } + i--; + if ( i > 0 ) + { + goto TCM_ID; + } + j++; + i=8; + goto TCM_ID; + +TCM_5: /* isolation complete.. */ +/* mbuf[32]=0; + printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */ + i=15; + j=mbuf[0]; + if ((j & 0x20) != 0) /* bit5=1:ID upto 7 */ + { + i=7; + } + if ((j & 0x06) == 0) /* IDvalid? */ + { + goto G2Q5; + } + k=mbuf[1]; +small_id: + m=1; + m <<= k; + if ((m & assignid_map) == 0) + { + goto G2Q_QUIN; + } + if ( k > 0 ) + { + k--; + goto small_id; + } +G2Q5: /* srch from max acceptable ID# */ + k=i; /* max acceptable ID# */ +G2Q_LP: + m=1; + m <<= k; + if ((m & assignid_map) == 0) + { + goto G2Q_QUIN; + } + if ( k > 0 ) + { + k--; + goto G2Q_LP; + } +G2Q_QUIN: /* k=binID#, */ + assignid_map |= m; + if ( k < 8 ) + { + quintet[0]=0x38; /* 1st dft ID<8 */ + } + else + { + quintet[0]=0x31; /* 1st ID>=8 */ + } + k &= 0x07; + quintet[1]=g2q_tab[k]; + + val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */ + m=quintet[0] << 8; + val |= m; + fun_scam(host,&val); + val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */ + m=quintet[1] << 8; + val |= m; + fun_scam(host,&val); + + goto TCM_SYNC; + +} + +void is870(unsigned long host,unsigned int wkport ) +{ + unsigned int tmport ; + unsigned char i,j,k,rmb; + unsigned short int m; + static unsigned char mbuf[512]; + static unsigned char satn[9] = { 0,0,0,0,0,0,0,6,6 }; + static unsigned char inqd[9] = { 0x12,0,0,0,0x24,0,0,0x24,6 }; + static unsigned char synn[6] = { 0x80,1,3,1,0x19,0x0e }; + static unsigned char synu[6] = { 0x80,1,3,1,0x0c,0x0e }; + static unsigned char synw[6] = { 0x80,1,3,1,0x0c,0x07 }; + static unsigned char wide[6] = { 0x80,1,2,3,1,0 }; + + sync_idu=0; + tmport=wkport+0x3a; + outb((unsigned char)(inb(tmport) | 0x10),tmport); + + for ( i = 0 ; i < 16 ; i ++ ) + { + if ((chip_veru[host] != 4) && (i > 7)) + { + break; + } + m=1; + m=m< 0x64 ) + { + continue; + } + if ( mbuf[4] > 0x0c ) + { + mbuf[4]=0x0c; + } + devspu[host][i] = mbuf[4]; + if ((mbuf[3] < 0x0d) && (rmb == 0)) + { + j=0xa0; + goto set_syn_ok; + } + if ( mbuf[3] < 0x1a ) + { + j=0x20; + goto set_syn_ok; + } + if ( mbuf[3] < 0x33 ) + { + j=0x40; + goto set_syn_ok; + } + if ( mbuf[3] < 0x4c ) + { + j=0x50; + goto set_syn_ok; + } + j=0x60; +set_syn_ok: + devspu[host][i] = (devspu[host][i] & 0x0f) | j; + } + tmport=wkport+0x3a; + outb((unsigned char)(inb(tmport) & 0xef),tmport); +} + +/* return non-zero on detection */ +int atp870u_detect(Scsi_Host_Template * tpnt) +{ + unsigned char irq,h,k; + unsigned long flags; + unsigned int base_io,error,tmport; + unsigned short index = 0; + unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3],host_id; + struct Scsi_Host * shpnt = NULL; + int count = 0; + static unsigned short devid[7]={0x8002,0x8010,0x8020,0x8030,0x8040,0x8050,0}; + + printk("aec671x_detect: \n"); + if (!pcibios_present()) + { + printk(" NO BIOS32 SUPPORT.\n"); + return count; + } + + tpnt->proc_dir = &proc_scsi_atp870u; + + for ( h = 0 ; h < 2 ; h++ ) + { + active_idu[h]=0; + wide_idu[h]=0; + host_idu[h]=0x07; + quhdu[h]=0; + quendu[h]=0; + pci_bus[h]=0; + pci_device_fn[h]=0xff; + chip_ver[h]=0; + last_cmd[h]=0xff; + in_snd[h]=0; + in_int[h]=0; + for ( k = 0 ; k < qcnt ; k++ ) + { + querequ[h][k]=0; + } + for ( k = 0 ; k < 16 ; k++ ) + { + curr_req[h][k]=0; + } + } + h=0; + while ( devid[h] != 0 ) + { + if (pcibios_find_device(0x1191,devid[h],index,&pci_bus[2],&pci_device_fn[2])) + { + h++; + index=0; + continue; + } + chip_ver[2]=0; + if ( devid[h] == 0x8002 ) + { + error = pcibios_read_config_byte(pci_bus[2],pci_device_fn[2],0x08,&chip_ver[2]); + if ( chip_ver[2] < 2 ) + { + goto nxt_devfn; + } + } + if ( devid[h] == 0x8010 ) + { + chip_ver[2]=0x04; + } + if ( pci_device_fn[2] < pci_device_fn[0] ) + { + pci_bus[1]=pci_bus[0]; + pci_device_fn[1]=pci_device_fn[0]; + chip_ver[1]=chip_ver[0]; + pci_bus[0]=pci_bus[2]; + pci_device_fn[0]=pci_device_fn[2]; + chip_ver[0]=chip_ver[2]; + } + else if ( pci_device_fn[2] < pci_device_fn[1] ) + { + pci_bus[1]=pci_bus[2]; + pci_device_fn[1]=pci_device_fn[2]; + chip_ver[1]=chip_ver[2]; + } +nxt_devfn: + index++; + if ( index > 3 ) + { + index=0; + h++; + } + } + for ( h=0; h < 2; h++ ) + { + if ( pci_device_fn[h] == 0xff ) + { + return count; + } + /* Found an atp870u/w. */ + error = pcibios_read_config_dword(pci_bus[h],pci_device_fn[h],0x10,&base_io); + error += pcibios_read_config_byte(pci_bus[h],pci_device_fn[h],0x3c,&irq); + error += pcibios_read_config_byte(pci_bus[h],pci_device_fn[h],0x49,&host_id); + + base_io &= 0xfffffff8; + printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" + ,h,base_io,irq); + ioportu[h]=base_io; + pciportu[h]=base_io + 0x20; + irqnumu[h]=irq; + host_id &= 0x07; + host_idu[h]=host_id; + chip_veru[h]=chip_ver[h]; + + tmport=base_io+0x22; + scam_on[h]=inb(tmport); + tmport += 0x0b; + global_map[h]=inb(tmport++); + ultra_map[h]=inw(tmport); + if ( ultra_map[h] == 0 ) + { + scam_on[h]=0x00; + global_map[h]=0x20; + ultra_map[h]=0xffff; + } + + shpnt = scsi_register(tpnt,4); + + save_flags(flags); + cli(); + if (request_irq(irq,atp870u_intr_handle, 0, "atp870u", NULL)) + { + printk("Unable to allocate IRQ for Acard controller.\n"); + goto unregister; + } + + tmport=base_io+0x3a; + k=(inb(tmport) & 0xf3) | 0x10; + outb(k,tmport); + outb((k & 0xdf),tmport); + mydlyu(0x8000); + outb(k,tmport); + mydlyu(0x8000); + tmport=base_io; + outb((host_id | 0x08),tmport); + tmport += 0x18; + outb(0,tmport); + tmport += 0x07; + while ((inb(tmport) & 0x80) == 0); + tmport -= 0x08; + inb(tmport); + tmport = base_io +1; + outb(8,tmport++); + outb(0x7f,tmport); + tmport = base_io + 0x11; + outb(0x20,tmport); + + tscam(h); + is870(h,base_io); + tmport=base_io+0x3a; + outb((inb(tmport) & 0xef),tmport); + + atp_host[h] = shpnt; + if ( chip_ver[h] == 4 ) + { + shpnt->max_id = 16; + } + shpnt->this_id = host_id; + shpnt->unique_id = base_io; + shpnt->io_port = base_io; + shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */ + shpnt->irq = irq; + restore_flags(flags); + request_region(base_io, 0x40,"atp870u"); /* Register the IO ports that we use */ + count++; + index++; + continue; +unregister: + scsi_unregister(shpnt); + restore_flags(flags); + index++; + continue; + } + + return count; +} + +/* The abort command does not leave the device in a clean state where + it is available to be used again. Until this gets worked out, we will + leave it commented out. */ + +int atp870u_abort(Scsi_Cmnd * SCpnt) +{ + unsigned char h,j; + unsigned int tmport; +/* printk(" atp870u_abort: \n"); */ + for ( h=0; h <= admaxu; h++ ) + { + if ( SCpnt->host == atp_host[h] ) + { + goto find_adp; + } + } + panic("Abort host not found !"); +find_adp: + printk(" workingu=%x last_cmd=%x ",workingu[h],last_cmd[h]); + printk(" quhdu=%x quendu=%x ",quhdu[h],quendu[h]); + tmport=ioportu[h]; + for ( j=0; j < 0x17; j++) + { + printk(" r%2x=%2x",j,inb(tmport++)); + } + tmport += 0x05; + printk(" r1c=%2x",inb(tmport)); + tmport += 0x03; + printk(" r1f=%2x in_snd=%2x ",inb(tmport),in_snd[h]); + tmport++; + printk(" r20=%2x",inb(tmport)); + tmport += 0x02; + printk(" r22=%2x \n",inb(tmport)); + return (SCSI_ABORT_SNOOZE); +} + +int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) +{ + unsigned char h; + /* + * See if a bus reset was suggested. + */ +/* printk("atp870u_reset: \n"); */ + for( h=0; h <= admaxu; h++ ) + { + if ( SCpnt->host == atp_host[h] ) + { + goto find_host; + } + } + panic("Reset bus host not found !"); +find_host: +/* SCpnt->result = 0x00080000; + SCpnt->scsi_done(SCpnt); + workingu[h]=0; + quhdu[h]=0; + quendu[h]=0; + return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */ + return (SCSI_RESET_SNOOZE); +} + +const char * +atp870u_info(struct Scsi_Host *notused) +{ + static char buffer[128]; + + strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 "); + + return buffer; +} + +int +atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) +{ + return (-ENOSYS); /* Currently this is a no-op */ +} + +#define BLS buffer + len + size +int +atp870u_proc_info(char *buffer, char **start, off_t offset, int length, + int hostno, int inout) +{ + struct Scsi_Host *HBAptr; + static u8 buff[512]; + int i; + int size = 0; + int len = 0; + off_t begin = 0; + off_t pos = 0; + + HBAptr = NULL; + for (i = 0; i < 2; i++) + { + if ((HBAptr = atp_host[i]) != NULL) + { + if (HBAptr->host_no == hostno) + { + break; + } + HBAptr = NULL; + } + } + + if (HBAptr == NULL) + { + size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno); + len += size; pos = begin + len; size = 0; + goto stop_output; + } + + if (inout == TRUE) /* Has data been written to the file? */ + { + return (atp870u_set_info(buffer, length, HBAptr)); + } + + if (offset == 0) + { + memset(buff, 0, sizeof(buff)); + } + + size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n"); + len += size; pos = begin + len; size = 0; + + size += sprintf(BLS, "\n"); + size += sprintf(BLS, "Adapter Configuration:\n"); + size += sprintf(BLS, " Base IO: %#.4x\n", HBAptr->io_port); + size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); + len += size; pos = begin + len; size = 0; + +stop_output: + *start = buffer + (offset - begin); /* Start of wanted data */ + len -= (offset - begin); /* Start slop */ + if (len > length) + { + len = length; /* Ending slop */ + } + + return (len); +} + +#include "sd.h" + +int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip) +{ + int heads, sectors, cylinders; + + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); + + if ( cylinders > 1024 ) + { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); + } + + ip[0] = heads; + ip[1] = sectors; + ip[2] = cylinders; + + return 0; +} + +#ifdef MODULE +Scsi_Host_Template driver_template = ATP870U; + +#include "scsi_module.c" +#endif + diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/atp870u.h linux/drivers/scsi/atp870u.h --- linux.vanilla/drivers/scsi/atp870u.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/atp870u.h Fri Dec 11 19:15:54 1998 @@ -0,0 +1,64 @@ +#ifndef _ATP870U_H + +/* $Id: atp870u.h,v 1.0 1997/05/07 15:09:00 root Exp root $ + * + * Header file for the ACARD 870U/W driver for Linux + * + * $Log: atp870u.h,v $ + * Revision 1.0 1997/05/07 15:09:00 root + * Initial revision + * + */ + +#include +#include + +/* I/O Port */ + +#define MAX_CDB 12 +#define MAX_SENSE 14 + +int atp870u_detect(Scsi_Host_Template *); +int atp870u_command(Scsi_Cmnd *); +int atp870u_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int atp870u_abort(Scsi_Cmnd *); +int atp870u_reset(Scsi_Cmnd *, unsigned int); +int atp870u_biosparam(Disk *, kdev_t, int*); +void send_s870(unsigned char); + +#define qcnt 32 +#define ATP870U_SCATTER 127 +#define ATP870U_CMDLUN 1 + +#ifndef NULL + #define NULL 0 +#endif + +extern struct proc_dir_entry proc_scsi_atp870u; + +extern const char *atp870u_info(struct Scsi_Host *); + +extern int atp870u_proc_info(char *, char **, off_t, int, int, int); + +#define ATP870U { NULL, NULL, \ + &proc_scsi_atp870u,/* proc_dir_entry */ \ + atp870u_proc_info, \ + NULL, \ + atp870u_detect, \ + NULL, \ + atp870u_info, \ + atp870u_command, \ + atp870u_queuecommand, \ + atp870u_abort, \ + atp870u_reset, \ + NULL, \ + atp870u_biosparam, \ + qcnt, \ + 7, \ + ATP870U_SCATTER, \ + ATP870U_CMDLUN, \ + 0, \ + 0, \ + ENABLE_CLUSTERING} + +#endif diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/dc390.h linux/drivers/scsi/dc390.h --- linux.vanilla/drivers/scsi/dc390.h Sun Jun 21 18:41:18 1998 +++ linux/drivers/scsi/dc390.h Sun Dec 6 17:52:31 1998 @@ -4,143 +4,55 @@ * Description: Device Driver for Tekram DC-390(T) PCI SCSI * * Bus Master Host Adapter * ***********************************************************************/ - -/* Kernel version autodetection */ +/* $Id: dc390.h,v 2.4 1998/11/05 10:16:42 garloff Exp $ */ #include -/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */ -#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) - -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,50) -#define VERSION_ELF_1_2_13 -#elseif LINUX_VERSION_CODE < ASC_LINUX_VERSION(1,3,95) -#define VERSION_1_3_85 -#else -#define VERSION_2_0_0 -#endif /* - * AMD 53C974 driver, header file + * DC390/AMD 53C974 driver, header file */ #ifndef DC390_H #define DC390_H +#define DC390_BANNER "Tekram DC390/AM53C974" +#define DC390_VERSION "2.0b1 1998/11/05" + #if defined(HOSTS_C) || defined(MODULE) -#ifdef VERSION_2_0_0 #include -#else -#include -#endif extern int DC390_detect(Scsi_Host_Template *psht); extern int DC390_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)); extern int DC390_abort(Scsi_Cmnd *cmd); - -#ifdef VERSION_2_0_0 extern int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags); -#else -extern int DC390_reset(Scsi_Cmnd *cmd); -#endif - -#ifdef VERSION_ELF_1_2_13 -extern int DC390_bios_param(Disk *disk, int devno, int geom[]); -#else extern int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]); -#endif #ifdef MODULE static int DC390_release(struct Scsi_Host *); #else -#define DC390_release NULL -#endif - -#ifndef VERSION_ELF_1_2_13 -extern struct proc_dir_entry proc_scsi_tmscsim; -extern int tmscsim_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); -#endif - -#ifdef VERSION_2_0_0 - -#define DC390_T { \ - NULL, /* *next */ \ - NULL, /* *usage_count */ \ - &proc_scsi_tmscsim, /* *proc_dir */ \ - tmscsim_proc_info, /* (*proc_info)() */ \ - "Tekram DC390(T) V1.11 Feb-05-1997", /* *name */ \ - DC390_detect, \ - DC390_release, /* (*release)() */ \ - NULL, /* *(*info)() */ \ - NULL, /* (*command)() */ \ - DC390_queue_command, \ - DC390_abort, \ - DC390_reset, \ - NULL, /* slave attach */\ - DC390_bios_param, \ - 10,/* can queue(-1) */ \ - 7, /* id(-1) */ \ - SG_ALL, \ - 2, /* cmd per lun(2) */ \ - 0, /* present */ \ - 0, /* unchecked isa dma */ \ - DISABLE_CLUSTERING \ - } +# define DC390_release NULL #endif +extern struct proc_dir_entry DC390_proc_scsi_tmscsim; +extern int DC390_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); -#ifdef VERSION_1_3_85 - -#define DC390_T { \ - NULL, /* *next */ \ - NULL, /* *usage_count */ \ - &proc_scsi_tmscsim, /* *proc_dir */ \ - tmscsim_proc_info, /* (*proc_info)() */ \ - "Tekram DC390(T) V1.11 Feb-05-1997", /* *name */ \ - DC390_detect, \ - DC390_release, /* (*release)() */ \ - NULL, /* *(*info)() */ \ - NULL, /* (*command)() */ \ - DC390_queue_command, \ - DC390_abort, \ - DC390_reset, \ - NULL, /* slave attach */\ - DC390_bios_param, \ - 10,/* can queue(-1) */ \ - 7, /* id(-1) */ \ - SG_ALL, \ - 2, /* cmd per lun(2) */ \ - 0, /* present */ \ - 0, /* unchecked isa dma */ \ - DISABLE_CLUSTERING \ - } -#endif - - -#ifdef VERSION_ELF_1_2_13 - -#define DC390_T { \ - NULL, \ - NULL, \ - "Tekram DC390(T) V1.11 Feb-05-1997",\ - DC390_detect, \ - DC390_release, \ - NULL, /* info */ \ - NULL, /* command, deprecated */ \ - DC390_queue_command, \ - DC390_abort, \ - DC390_reset, \ - NULL, /* slave attach */\ - DC390_bios_param, \ - 10,/* can queue(-1) */ \ - 7, /* id(-1) */ \ - 16,/* old (SG_ALL) */ \ - 2, /* cmd per lun(2) */ \ - 0, /* present */ \ - 0, /* unchecked isa dma */ \ - DISABLE_CLUSTERING \ - } -#endif +#define DC390_T { \ + proc_dir: &DC390_proc_scsi_tmscsim, \ + proc_info: DC390_proc_info, \ + name: DC390_BANNER " V" DC390_VERSION, \ + detect: DC390_detect, \ + release: DC390_release, \ + queuecommand: DC390_queue_command, \ + abort: DC390_abort, \ + reset: DC390_reset, \ + bios_param: DC390_bios_param, \ + can_queue: 17, \ + this_id: 7, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 8, \ + use_clustering: DISABLE_CLUSTERING \ + } #endif /* defined(HOSTS_C) || defined(MODULE) */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c --- linux.vanilla/drivers/scsi/hosts.c Sun Dec 6 00:14:43 1998 +++ linux/drivers/scsi/hosts.c Fri Dec 11 19:11:10 1998 @@ -169,6 +169,10 @@ #include "ppa.h" #endif +#ifdef CONFIG_SCSI_ACARD +#include "atp870u.h" +#endif + #ifdef CONFIG_SCSI_SUNESP #include "esp.h" #endif @@ -181,6 +185,10 @@ #include "gdth.h" #endif +#ifdef CONFIG_SCSI_INITIO +#include "ini9100u.h" +#endif + #ifdef CONFIG_SCSI_DEBUG #include "scsi_debug.h" #endif @@ -322,11 +330,17 @@ #ifdef CONFIG_SCSI_PPA PPA, #endif +#ifdef CONFIG_SCSI_ACARD + ATP870U, +#endif #ifdef CONFIG_SCSI_SUNESP SCSI_SPARC_ESP, #endif #ifdef CONFIG_SCSI_GDTH GDTH, +#endif +#ifdef CONFIG_SCSI_INITIO + INI9100U, #endif #ifdef CONFIG_BLK_DEV_IDESCSI IDESCSI, diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i91uscsi.c linux/drivers/scsi/i91uscsi.c --- linux.vanilla/drivers/scsi/i91uscsi.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/i91uscsi.c Wed Dec 9 09:52:16 1998 @@ -0,0 +1,3332 @@ +/************************************************************************** + * Initio 9100 device driver for Linux. + * + * Copyright (c) 1994-1998 Initio Corporation + * All rights reserved. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ************************************************************************ + Module: i91uscsi.c + Description: PCI I/F for INI-910 SCSI Bus Master Controller + Revision History: + 11/09/94 Tim Chen, Initiali Version 0.90A + 01/17/95 TC, release ver 1.01 + 02/09/95 TC modify ReadPCIConfig, try both mechanisms; + 02/15/95 TC add support for INI-9100W + 06/04/96 HC, Change to fit LINUX from jaspci.c + 11/18/96 HC, Port for tulip + 07/08/98 hc, Support 0002134A + 07/23/98 wh, Change the abort_srb routine. + 09/16/98 hl, Support ALPHA, Rewrite the returnNumberAdapters <01> +**********************************************************************/ + +#define DEBUG_INTERRUPT 0 +#define DEBUG_QUEUE 0 +#define DEBUG_STATE 0 +#define INT_DISC 0 + + +#ifndef CVT_LINUX_VERSION +#define CVT_LINUX_VERSION(V,P,S) (V * 65536 + P * 256 + S) +#endif +#if 0 +#include "../block/blk.h" +#include +#include "scsi.h" +#include "hosts.h" +#include "constants.h" +#include "i91uscsi.h" + +#endif +#if 1 +#include +/*#include */ +#include +/*#include "hosts.h"*/ +/*#include "scsi.h"*/ +#include "i91uscsi.h" +#endif + +/*--- external functions --*/ +static void tul_se2_wait(void); +/*extern void tul_do_pause(unsigned int);*/ + +/*--- forward refrence ---*/ +static SCB *tul_find_busy_scb(HCS *pCurHcb, WORD tarlun); +static SCB *tul_find_done_scb(HCS *pCurHcb); + +static int tulip_main(HCS *pCurHcb); +/* static int init_tulip(HCS *pCurHcb, SCB *pCurScb, int ch_idx);*/ + +static int tul_next_state(HCS *pCurHcb); +static int tul_state_1(HCS *pCurHcb); +static int tul_state_2(HCS *pCurHcb); +static int tul_state_3(HCS *pCurHcb); +static int tul_state_4(HCS *pCurHcb); +static int tul_state_5(HCS *pCurHcb); +static int tul_state_6(HCS *pCurHcb); +static int tul_state_7(HCS *pCurHcb); +static int tul_xfer_data_in(HCS *pCurHcb); +static int tul_xfer_data_out(HCS *pCurHcb); +static int tul_xpad_in(HCS *pCurHcb); +static int tul_xpad_out(HCS *pCurHcb); +static int tul_status_msg(HCS *pCurHcb); +/*static int tul_bad_seq(HCS *pCurHcb); 10/13/97 */ + +static int tul_msgin(HCS *pCurHcb); +static int tul_msgin_sync(HCS *pCurHcb); +static int tul_msgin_accept(HCS *pCurHcb); +static int tul_msgout_reject(HCS *pCurHcb); +static int tul_msgin_extend(HCS *pCurHcb); + +static int tul_msgout_ide(HCS *pCurHcb); +static int tul_msgout_abort_targ(HCS *pCurHcb); +static int tul_msgout_abort_tag(HCS *pCurHcb); + +static int tul_bus_device_reset(HCS *pCurHcb); +static void tul_select_atn(HCS *pCurHcb, SCB *pCurScb); +static void tul_select_atn3(HCS *pCurHcb, SCB *pCurScb); +static void tul_select_atn_stop(HCS *pCurHcb, SCB *pCurScb); +//static int tul_reset_scsi(HCS *pCurHcb, int seconds); +static int int_tul_busfree(HCS *pCurHcb); +#if 0 +static int int_tul_scsi_rst(HCS *pCurHcb); +#else +int int_tul_scsi_rst(HCS *pCurHcb); +#endif +static int int_tul_bad_seq(HCS *pCurHcb); +static int int_tul_resel(HCS *pCurHcb); +static int tul_sync_done(HCS *pCurHcb); +static int wdtr_done(HCS *pCurHcb); +static int wait_tulip(HCS *pCurHcb); +static int tul_wait_done_disc(HCS *pCurHcb); +static int tul_wait_disc(HCS *pCurHcb); +/*static int tul_msgin_par_err(HCS *pCurHcb);*/ +static void tulip_scsi(HCS *pCurHcb); +static int tul_post_scsi_rst(HCS *pCurHcb); + +static void tul_se2_ew_en(WORD CurBase); +static void tul_se2_ew_ds(WORD CurBase); +static int tul_se2_rd_all(WORD CurBase); +static void tul_se2_update_all(WORD CurBase); /* setup default pattern */ +static void tul_read_eeprom(WORD CurBase); + + /* ---- EXTERNAL VARIABLES ---- */ +HCS tul_hcs[MAX_SUPPORTED_ADAPTERS]; + /* ---- INTERNAL VARIABLES ---- */ +static INI_ADPT_STRUCT i91u_adpt[MAX_SUPPORTED_ADAPTERS]; + +/*NVRAM nvram, *nvramp = &nvram;*/ +static NVRAM i91unvram; +static NVRAM *i91unvramp; + + +/* HCSINFO HcsInfo[ MAX_PCI_DEVICES * MAX_PCI_CHANL ]; ??? */ +/*HCSINFO HcsInfo[ MAX_PCI_CHANL ];*/ + +static UCHAR i91udftNvRam[64] = { /*----------- header -----------*/ + 0x25, 0xc9, /* Signature */ + 0x40, /* Size */ + 0x01, /* Revision */ + /* -- Host Adapter Structure -- */ + 0x95, /* ModelByte0 */ + 0x00, /* ModelByte1 */ + 0x00, /* ModelInfo */ + 0x01, /* NumOfCh */ + NBC1_DEFAULT, /* BIOSConfig1 */ + 0, /* BIOSConfig2 */ + 0, /* HAConfig1 */ + 0, /* HAConfig2 */ + /* SCSI channel 0 and target Structure */ + 7, /* SCSIid */ + NCC1_DEFAULT, /* SCSIconfig1 */ + 0, /* SCSIconfig2 */ + 0x10, /* NumSCSItarget*/ + + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + + /* SCSI channel 1 and target Structure */ + 7, /* SCSIid */ + NCC1_DEFAULT, /* SCSIconfig1 */ + 0, /* SCSIconfig2 */ + 0x10, /* NumSCSItarget*/ + + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 }; /* - CheckSum - */ + + +static UCHAR tul_rate_tbl[8] = /* fast 20 */ + { + /* nanosecond devide by 4 */ + 12, /* 50ns, 20M */ + 18, /* 75ns, 13.3M */ + 25, /* 100ns, 10M */ + 31, /* 125ns, 8M */ + 37, /* 150ns, 6.6M */ + 43, /* 175ns, 5.7M */ + 50, /* 200ns, 5M */ + 62 /* 250ns, 4M */ + }; + +#if 0 /* <01> */ +static UCHAR bi91uPCI_mechanism = 0; /* The mechanism used by read PCI */ +#endif +extern int tul_num_ch; + + +#if 1 +static void tul_do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */ +{ + unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */ + + while (jiffies < the_time) + ; +} +#endif + +/*-- forward reference --*/ +#if 0 +static void tul_snapdump(ULONG *ptr, int length) +{ + int col, row; + int i, idx; + + row = length / 16 + ((length % 16) ? 1 : 0); + idx = 0; + printk("\n\r"); + for (i = 0; i < row; i++) + { + printk("Offset %04x at %08lx : ", idx, (ULONG) ptr); + for (col = 0; (col < 2) & (idx < length); col++, idx+=4) + { + printk(" %08lx", *ptr++); + } + printk(" -"); + for (col = 0; (col < 2) & (idx < length); col++, idx+=4) + { + printk(" %08lx", *ptr++); + } + printk("\n\r"); + } +} +#endif +/*--- forward reference ---*/ + +/******************************************************************* + Use memeory refresh time ~ 15us * 2 +********************************************************************/ +void tul_se2_wait() +{ +#if 0 + ULONG i; + i = jiffies + (30 * HZ / 100000); + while (jiffies , i++); +#else + UCHAR readByte; + + readByte = TUL_RD( 0, 0x61 ); + if ( (readByte & 0x10) == 0x10) { + for (; ;) { + readByte = TUL_RD( 0, 0x61 ); + if ( (readByte & 0x10) == 0x10) + break; + } + for (;;) { + readByte = TUL_RD( 0, 0x61 ); + if ( (readByte & 0x10) != 0x10) + break; + } + } else { + for (; ; ) { + readByte = TUL_RD( 0, 0x61 ); + if ( (readByte & 0x10) == 0x10) + break; + } + for (; ; ) { + readByte = TUL_RD( 0, 0x61 ); + if ( (readByte & 0x10) != 0x10) + break; + } + } +#endif +} + + +/****************************************************************** + Input: instruction for Serial E2PROM + + EX: se2_rd(0 call se2_instr() to send address and read command + + StartBit OP_Code Address Data + --------- -------- ------------------ ------- + 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0 + + +----------------------------------------------------- + | + CS -----+ + +--+ +--+ +--+ +--+ +--+ + ^ | ^ | ^ | ^ | ^ | + | | | | | | | | | | + CLK -------+ +--+ +--+ +--+ +--+ +-- + (leading edge trigger) + + +--1-----1--+ + | SB OP | OP A5 A4 + DI ----+ +--0------------------ + (address and cmd sent to nvram) + + -------------------------------------------+ + | + DO +--- + (data sent from nvram) + + +******************************************************************/ +void tul_se2_instr(WORD CurBase, UCHAR instr) +{ + int i; + UCHAR b; + + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* cs+start bit */ + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK | SE2DO); /* +CLK */ + tul_se2_wait(); + + for ( i = 0; i < 8; i++) { + if (instr & 0x80) + b = SE2CS | SE2DO; /* -CLK+dataBit */ + else + b = SE2CS ; /* -CLK */ + TUL_WR(CurBase + TUL_NVRAM, b); + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, b | SE2CLK); /* +CLK */ + tul_se2_wait(); + instr <<= 1; + } + TUL_WR(CurBase + TUL_NVRAM, SE2CS ); /* -CLK */ + tul_se2_wait(); + return; +} + + +/****************************************************************** + Function name : tul_se2_ew_en + Description : Enable erase/write state of serial EEPROM +******************************************************************/ +void tul_se2_ew_en(WORD CurBase) +{ + tul_se2_instr(CurBase, 0x30); /* EWEN */ + TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */ + tul_se2_wait(); + return; +} + + +/************************************************************************ + Disable erase/write state of serial EEPROM +*************************************************************************/ +void tul_se2_ew_ds(WORD CurBase) +{ + tul_se2_instr(CurBase, 0); /* EWDS */ + TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */ + tul_se2_wait(); + return; +} + + +/****************************************************************** + Input :address of Serial E2PROM + Output :value stored in Serial E2PROM +*******************************************************************/ +USHORT tul_se2_rd(WORD CurBase, ULONG adr) +{ + UCHAR instr, readByte; + USHORT readWord; + int i; + + instr = (UCHAR)( adr | 0x80 ); + tul_se2_instr(CurBase, instr); /* READ INSTR */ + readWord = 0; + + for ( i = 15; i >= 0; i--) { + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK ); /* +CLK */ + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, SE2CS ); /* -CLK */ + + /* sample data after the following edge of clock */ + readByte = TUL_RD(CurBase, TUL_NVRAM); + readByte &= SE2DI; + readWord += (readByte << i); + tul_se2_wait(); /* 6/20/95 */ + } + + TUL_WR(CurBase + TUL_NVRAM, 0 ); /* no chip select */ + tul_se2_wait(); + return readWord; +} + + +/****************************************************************** + Input: new value in Serial E2PROM, address of Serial E2PROM +*******************************************************************/ +void tul_se2_wr(WORD CurBase, UCHAR adr, USHORT writeWord) +{ + UCHAR readByte; + UCHAR instr; + int i; + + instr = (UCHAR)( adr | 0x40 ); + tul_se2_instr(CurBase, instr); /* WRITE INSTR */ + for ( i = 15; i >= 0; i--) { + if (writeWord & 0x8000) + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2DO); /* -CLK+dataBit 1 */ + else + TUL_WR(CurBase + TUL_NVRAM, SE2CS ); /* -CLK+dataBit 0 */ + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK ); /* +CLK */ + tul_se2_wait(); + writeWord <<= 1; + } + TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* -CLK */ + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */ + tul_se2_wait(); + + TUL_WR(CurBase + TUL_NVRAM, SE2CS); /* +CS */ + tul_se2_wait(); + + for (; ; ) { + TUL_WR(CurBase + TUL_NVRAM, SE2CS | SE2CLK); /* +CLK */ + tul_se2_wait(); + TUL_WR(CurBase + TUL_NVRAM, SE2CS ); /* -CLK */ + tul_se2_wait(); + if ((readByte = TUL_RD(CurBase, TUL_NVRAM )) & SE2DI ) + break; /* write complete */ + } + TUL_WR(CurBase + TUL_NVRAM, 0); /* -CS */ + return; +} + + +/*********************************************************************** + Read SCSI H/A configuration parameters from serial EEPROM +************************************************************************/ +int tul_se2_rd_all(WORD CurBase) +{ + int i; + ULONG chksum = 0; + USHORT *np; + + i91unvramp = &i91unvram; + np = (USHORT * )i91unvramp; + for ( i = 0; i < 32; i++) { + *np++ = tul_se2_rd(CurBase, i); + } + + /*--------------------Is signature "ini" ok ? ----------------*/ + if ( i91unvramp->NVM_Signature != INI_SIGNATURE ) + return - 1; +#if 0 + /*--------------------Is Model# byte 1 "91" ok ? ----------------*/ +// if ( i91unvramp->ModelByte0 != 0x91) /* 4: Model number (byte 0) */ +// return - 1; +#endif + /*---------------------- Is ckecksum ok ? ----------------------*/ + np = (USHORT * )i91unvramp; + for ( i = 0; i < 31; i++) + chksum += *np++; + if ( i91unvramp->NVM_CheckSum != (USHORT)chksum ) + return - 1; + return 1; +} + + +/*********************************************************************** + Update SCSI H/A configuration parameters from serial EEPROM +************************************************************************/ +void tul_se2_update_all(WORD CurBase) /* setup default pattern */ +{ + int i; + ULONG chksum = 0; + USHORT * np, *np1; + + i91unvramp = &i91unvram; + /* Calculate checksum first */ + np = (USHORT * )i91udftNvRam; + for ( i = 0; i < 31; i++) + chksum += *np++; + *np = (USHORT)chksum; + tul_se2_ew_en(CurBase); /* Enable write */ + + np = (USHORT * )i91udftNvRam; + np1 = (USHORT * )i91unvramp; + for ( i = 0; i < 32; i++, np++, np1++) { + if (*np != *np1) { + tul_se2_wr(CurBase, i, *np); + } + } + + tul_se2_ew_ds(CurBase); /* Disable write */ + return; +} + +/************************************************************************* + Function name : read_eeprom +**************************************************************************/ +void tul_read_eeprom(WORD CurBase) +{ + UCHAR gctrl; + + i91unvramp = &i91unvram; + /*------Enable EEProm programming ---*/ + gctrl = TUL_RD(CurBase, TUL_GCTRL); + TUL_WR(CurBase + TUL_GCTRL, gctrl | TUL_GCTRL_EEPROM_BIT ); + if ( tul_se2_rd_all(CurBase) != 1 ) { + tul_se2_update_all(CurBase); /* setup default pattern */ + tul_se2_rd_all(CurBase); /* load again */ + } + /*------ Disable EEProm programming ---*/ + gctrl = TUL_RD(CurBase, TUL_GCTRL); + TUL_WR(CurBase + TUL_GCTRL, gctrl & ~TUL_GCTRL_EEPROM_BIT ); +} /* read_eeprom */ + + +#if 0 /* <01> */ +static UBYTE i91uGetPCIMechanism(void) +{ + int bus, device; + unsigned long lConfigAddress, lConfigData; + + lConfigAddress = 0x80000000; + for (bus = 0; bus < 255; bus++) + { + lConfigAddress &= 0xFFFF0000; + for (device = 0; device < 21; device++) + { + outl(lConfigAddress, 0xCF8); /*Write config_address,enable*/ + lConfigData = inl(0xCFC); /* Read config_data */ + if ((lConfigData & 0xFFFF) == 0x1101) + return (1); /* Mechanism 1 */ + if ((lConfigData & 0xFFFF) == 0x134A) + return (1); /* Mechanism 1 */ + lConfigAddress += 0x800; + } + lConfigAddress += 0x10000; + } + return (2); /* Mechanism 2 */ +} + +#if 1 + +/*****************************************************************************/ +static unsigned long i91uReadPCIConfig( unsigned char BusNum, + unsigned char DeviceNum, + unsigned char RegNum) +{ + HOST_ADR HostAdr; + CONFIG_ADR ConfigAdr; + unsigned long ConfigData; + +#if BIOS32 + REGS Regs; + + if (CallBIOS32) + { /* PCI BIOS32 exists, use it */ + Regs.eax.byte.ah = PCI_FUNCTION_ID; + Regs.eax.byte.al = READ_CONFIG_DWORD; + Regs.ebx.byte.bh = BusNum; + Regs.ebx.byte.bl = DeviceNum & 0xFC; + Regs.edi.word.di = RegNum; + if (PCIBIOS32Call(CallBIOS32, &Regs)) + { + return(-1); /* Read configuration error */ + } + if (Regs.eax.byte.ah != SUCCESSFUL) + { /* Check return code */ + return(-1); /* Read configuration error */ + } + return(Regs.ecx.ecx); + } +#endif + if (bi91uPCI_mechanism == 0) + bi91uPCI_mechanism = i91uGetPCIMechanism(); + if (bi91uPCI_mechanism == 1) + { /* Mechanism #1 */ + ConfigAdr.lConfigAdr = 0; + ConfigAdr.sConfigAdr.RegNum = RegNum & 0xFC; /* Get rid of bit 0&1 */ + ConfigAdr.sConfigAdr.DeviceNum = DeviceNum; + ConfigAdr.sConfigAdr.BusNum = BusNum; + ConfigAdr.sConfigAdr.Enable = 1; + outl(ConfigAdr.lConfigAdr, 0xCF8); /*Write config_address,enable*/ + ConfigData = inl(0xCFC); /* Read config_data */ + ConfigAdr.lConfigAdr = 0; + outl(ConfigAdr.lConfigAdr, 0xCF8); /*Write config_address,disable*/ + /*if (ConfigData != -1) Read success */ + return(ConfigData); + } + else + { /* Mechanism #2 */ + if (DeviceNum >= 16) /* Mechanism #2 only support 16 */ + return(-1); /* devices C000- CF00 */ + HostAdr.sHostAdr.RegNum = RegNum & 0xFC; /* Get rid of bit 0&1 */ + HostAdr.sHostAdr.DeviceNum = 0xC0 | (DeviceNum & 0xF); + HostAdr.sHostAdr.Reserved = 0x0000; + outb(BusNum, 0xCFA);/* Setup forward address */ + outb(0xC0, 0xCF8); /* Configuration mapping enable */ +/* outb(0xC0, 0xCFA); Configuration mapping enable */ + ConfigData = inl(HostAdr.lHostAdr);/* Read configuration data */ + outb(0, 0xCFA); /* Setup forward address */ + outb(0, 0xCF8); /* Configuration mapping disable*/ + return(ConfigData); + } +} + +#else + +/************************************************************************* + Function name : ReadPCIConfig + Description : This function return configuration space's register value. + Input : + BusNum - Bus number + DeviceNum - Device number + RegNum - Register number + Output : None. + Return : Register value +**************************************************************************/ +unsigned long ReadPCIConfig( unsigned char BusNum, + unsigned char DeviceNum, + unsigned char RegNum) +{ + HOST_ADR HostAdr; + CONFIG_ADR ConfigAdr; + unsigned long ConfigData; + + if (bPCI_mechanism == 1) { + /*------------------ Mechanism #1 ------------------------- */ + ConfigAdr.lConfigAdr = 0; + ConfigAdr.sConfigAdr.RegNum = RegNum & 0xFC; /* Get rid of bit 0&1 */ + ConfigAdr.sConfigAdr.DeviceNum = DeviceNum; + ConfigAdr.sConfigAdr.BusNum = BusNum; + ConfigAdr.sConfigAdr.Enable = 1; + outpl( 0xCF8, ConfigAdr.lConfigAdr); /* Write config_address, enable */ + + ConfigData = inpl( 0xCFC ); /* Read config_data */ + ConfigAdr.lConfigAdr = 0; + outpl( 0xCF8, ConfigAdr.lConfigAdr); /* Write config_address, disable*/ + return(ConfigData); + } else { + /*------------------ Mechanism #2 ------------------------- */ + if (DeviceNum >= 16) /* Mechanism #2 only support 16 */ + return(0); /* devices C000- CF00 */ + HostAdr.lHostAdr = 0; + HostAdr.sHostAdr.RegNum = RegNum & 0xFC; /* Get rid of bit 0&1 */ + HostAdr.sHostAdr.DeviceNum = 0xC0 | (DeviceNum & 0xF); + outp(0xCFA, BusNum); /* Setup forward address */ + outp(0xCF8, 0xC0); /* Configuration mapping enable */ + ConfigData = inpl(HostAdr.lHostAdr); /* Read configuration data */ + outp(0xCFA, 0); /* Setup forward address */ + outp(0xCF8, 0); /* Configuration mapping disable*/ + return(ConfigData); + } +} +#endif +#endif + +int Addi91u_into_Adapter_table(WORD wBIOS, WORD wBASE, BYTE bInterrupt, + BYTE bBus, BYTE bDevice) +{ + int i, j; + + for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) + { + if (i91u_adpt[i].ADPT_BIOS < wBIOS) + continue; + if (i91u_adpt[i].ADPT_BIOS == wBIOS) + { + if (i91u_adpt[i].ADPT_BASE == wBASE) + if (i91u_adpt[i].ADPT_Bus != 0xFF) + return(FAILURE); + else + if (i91u_adpt[i].ADPT_BASE < wBASE) + continue; + } + for (j = MAX_SUPPORTED_ADAPTERS - 1; j > i; j--) + { + i91u_adpt[j].ADPT_BASE = i91u_adpt[j-1].ADPT_BASE; + i91u_adpt[j].ADPT_INTR = i91u_adpt[j-1].ADPT_INTR; + i91u_adpt[j].ADPT_BIOS = i91u_adpt[j-1].ADPT_BIOS; + i91u_adpt[j].ADPT_Bus = i91u_adpt[j-1].ADPT_Bus; + i91u_adpt[j].ADPT_Device = i91u_adpt[j-1].ADPT_Device; + } + i91u_adpt[i].ADPT_BASE = wBASE; + i91u_adpt[i].ADPT_INTR = bInterrupt; + i91u_adpt[i].ADPT_BIOS = wBIOS; + i91u_adpt[i].ADPT_Bus = bBus; + i91u_adpt[i].ADPT_Device = bDevice; + return(SUCCESSFUL); + } + return(FAILURE); +} + +void init_i91uAdapter_table(void) +{ + int i; + + for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) + { /* Initialize adapter structure */ + i91u_adpt[i].ADPT_BIOS = 0xffff; + i91u_adpt[i].ADPT_BASE = 0xffff; + i91u_adpt[i].ADPT_INTR = 0xff; + i91u_adpt[i].ADPT_Bus = 0xff; + i91u_adpt[i].ADPT_Device = 0xff; + } + return; +} + +#if 0 /* <01> */ +int tul_ReturnNumberOfAdapters(void) +{ + long dRegValue; + int i, iAdapters; + BYTE bPCIBusNum, bTotalBusNum, bInterrupt, bPCIDeviceNum; + WORD wBIOS, wBASE; + + for (i = 0; i < MAX_SUPPORTED_ADAPTERS; i++) + { /* Initialize adapter structure */ + i91u_adpt[i].ADPT_BIOS = 0xffff; + i91u_adpt[i].ADPT_BASE = 0xffff; + i91u_adpt[i].ADPT_INTR = 0xff; + i91u_adpt[i].ADPT_Bus = 0xff; + i91u_adpt[i].ADPT_Device = 0xff; + } + + bTotalBusNum = 0xFF; /* Search 255 buses */ + +/* bi91uPCI_mechanism = 1; 02/05/95 */ + + for (bPCIBusNum = 0, iAdapters = 0; bPCIBusNum < bTotalBusNum; bPCIBusNum++) + { /* Scan all the buses */ + for (bPCIDeviceNum=0; bPCIDeviceNum < MAX_PCI_DEVICES; bPCIDeviceNum++) + { /* Find all jasmine devices */ + if (iAdapters >= MAX_SUPPORTED_ADAPTERS) + break; /* Never greater than maximum */ + dRegValue = i91uReadPCIConfig(bPCIBusNum, bPCIDeviceNum, TUL_PVID); + if ((dRegValue & 0xFFFF) != INI_VENDOR_ID) + { +#if 1 + if ((dRegValue & 0xFFFF) != 0x134A) /* 07/08/98 */ + continue; +#else + if (bPCI_MechanismDone) + continue; + bi91uPCI_mechanism = 2; /* 02/05/95 */ + dRegValue =i91uReadPCIConfig(bPCIBusNum,bPCIDeviceNum,TUL_PVID); + if ((dRegValue & 0xFFFF) != INI_VENDOR_ID) + { + bi91uPCI_mechanism = 1; /* 02/05/95 */ + continue; /* Wrong vendor ID */ + } +#endif + } +/* bPCI_MechanismDone = 1;*/ + if ((((dRegValue >> 16) & 0xFF00) != I950_DEVICE_ID) && + (((dRegValue >> 16) & 0xFF00) != I940_DEVICE_ID) && + (((dRegValue >> 16) & 0xFF00) != 0x0002) && /* 07/08/98 */ + (((dRegValue >> 16) & 0xFF00) != I935_DEVICE_ID)) + continue; /* Wrong device ID */ + /* Read base address */ + dRegValue = i91uReadPCIConfig(bPCIBusNum, bPCIDeviceNum, TUL_PBAD); + if (dRegValue == -1) + { /* Check return code */ + printk("\n\ri91u: Jasmine read configuration error.\n"); + return(0); /* Read configuration space error */ + } + if ((dRegValue & 0x01) == 0) + { /* Base address for memory */ + printk("\n\ri91u: Wrong base address setting at -> Bus: %d; Device: %d", bPCIBusNum, bPCIDeviceNum); + continue; /* PCI configuration setup error*/ + } + + wBASE = (WORD) (dRegValue & 0xFFFE); + /* Now read the BIOS address */ + dRegValue = i91uReadPCIConfig(bPCIBusNum, bPCIDeviceNum, TUL_SDCFG0); + wBIOS = (UWORD) (dRegValue & 0xFF); + if (((dRegValue & 0xFF00) >> 8) == 0xFF) + dRegValue = 0; + wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8)); + /* Now read the interrupt line */ + dRegValue = i91uReadPCIConfig(bPCIBusNum, bPCIDeviceNum, TUL_PINTL); + bInterrupt = dRegValue & 0xFF;/* Assign interrupt line */ + + if (Addi91u_into_Adapter_table(wBIOS, wBASE, bInterrupt, bPCIBusNum, + bPCIDeviceNum) == SUCCESSFUL) + iAdapters++; + } + } + return (iAdapters); +} +#endif + +void tul_stop_bm(HCS *pCurHcb) +{ + + if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) + { /* if DMA xfer is pending, abort DMA xfer */ + TUL_WR(pCurHcb->HCS_Base+ TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO); + /* wait Abort DMA xfer done */ + while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0); + } + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); +} + +/***************************************************************************/ +void get_tulipPCIConfig(HCS *pCurHcb, int ch_idx) +{ + pCurHcb->HCS_Base = i91u_adpt[ch_idx].ADPT_BASE; /* Supply base address */ + pCurHcb->HCS_BIOS = i91u_adpt[ch_idx].ADPT_BIOS; /* Supply BIOS address */ + pCurHcb->HCS_Intr = i91u_adpt[ch_idx].ADPT_INTR; /* Supply interrupt line*/ + return; +} + +/***************************************************************************/ +int tul_reset_scsi(HCS *pCurHcb, int seconds) +{ +#if 0 + printk("i91u: enter tul_reset_scsi.\n"); +#endif + + TUL_WR( pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_BUS); + + while(!((pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base,TUL_SInt)) & TSS_SCSIRST_INT)); + /* reset tulip chip */ + + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, 0); + + /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */ + /* SONY 5200 tape drive won't work if only stall for 1 sec */ +#if 1 + tul_do_pause(seconds*100); +#else + for (i = 0; i < 66666; i++) /* wait 2 sec */ + tul_se2_wait(); /* wait 30 us */ +#endif + + TUL_RD(pCurHcb->HCS_Base,TUL_SInt); + +#if 0 + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0,TSC_RST_CHIP); + /* program HBA's SCSI ID */ + TUL_WR( hcsp->Base+TUL_SScsiId,hcsp->HaId << 4); + + /* Enable Initiator Mode ,phase latch,alternate sync period mode, + disable SCSI reset */ + if (hcsp->Config & HCC_EN_PAR) + TUL_WR(hcsp->Base+TUL_SConfig, TSC_INITDEFAULT | TSC_EN_SCSI_PAR); + else + TUL_WR(hcsp->Base+TUL_SConfig, TSC_INITDEFAULT ); + + /*Synchronous data transfer rate + Sync. period 0 -> 100 ns for 10 MHz + Sync. offset 0 -> Async. operation */ + /* Initialize the sync. xfer register values to an narrow, async. xfer */ + TUL_WR(hcsp->Base+TUL_SPeriodOffset, 0x70); + + /* selection time out = 250 ms */ + TUL_WR(hcsp->Base+TUL_STimeOut, 153); + +#if 1 + /* Initialize the sync. xfer register values to an async. xfer */ + tcsp = &hcsp->HCS_Tcs[0]; + for (i = 0; i < pCurHcb->HCS_MaxTar; tcsp++, i++) { + tcsp->Flags &= ~(TCF_SYNC_DONE|TCF_WDTR_DONE); + } /* for */ +#endif + + if (hwInitialized) { + /* Post_scsi_rst: clean up active SCB */ + /* clear sync done flag */ + dbgPrint0(0,"tul_reset_scsi:call tul_post_scsi_rst\n"); + tul_post_scsi_rst(hcsp); + } + +#if NEWCHIP_BUG0 + TUL_WR(hcsp->Base+TUL_STest1, 0x22); +#endif + + return; + + /* Post_scsi_rst: clean up active SCB */ + pCurHcb->HCS_ActScb = 0; + pCurHcb->HCS_ActTcs = 0; + + while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) + { +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + tul_append_done_scb(pCurHcb, pCurScb); + } + /* clear sync done flag */ + pCurTcb = &pCurHcb->HCS_Tcs[0]; + for (i = 0; i < 8; pCurTcb++, i++) { + pCurTcb->TCS_Flags &= (BYTE)~(TCF_SYNC_DONE|TCF_WDTR_DONE); + /* Initialize the sync. xfer register values to an asyn xfer */ + pCurTcb->TCS_JS_Period = 5; /* Async xfer before nego. */ + pCurTcb->TCS_JS_Offset = 0x0; /* Async xfer before nego. */ + pCurTcb->TCS_JS_Config3 = 0x8; /* Fast clock */ + } /* for */ + +#endif + return (SCSI_RESET_SUCCESS); +} + +/***************************************************************************/ +int init_tulip(HCS *pCurHcb, SCB *scbp, int tul_num_scb, BYTE *pbBiosAdr, int seconds) +{ + int i; + WORD *pwFlags; + BYTE *pbHeads; + SCB *pTmpScb, *pPrevScb = NULL; + + pCurHcb->HCS_NumScbs = tul_num_scb; + pCurHcb->HCS_Semaph = 1; + pCurHcb->HCS_JSStatus0 = 0; + pCurHcb->HCS_Scb = scbp; + pCurHcb->HCS_NxtPend = scbp; + pCurHcb->HCS_NxtAvail = scbp; + for (i = 0, pTmpScb = scbp; i < tul_num_scb; i++, pTmpScb++) + { + pTmpScb->SCB_TagId = i; + if (i != 0) + pPrevScb->SCB_NxtScb = pTmpScb; + pPrevScb = pTmpScb; + } + pPrevScb->SCB_NxtScb = NULL; + pCurHcb->HCS_ScbEnd = pTmpScb; + pCurHcb->HCS_FirstAvail = scbp; + pCurHcb->HCS_LastAvail = pPrevScb; + pCurHcb->HCS_FirstPend = NULL; + pCurHcb->HCS_LastPend = NULL; + pCurHcb->HCS_FirstBusy = NULL; + pCurHcb->HCS_LastBusy = NULL; + pCurHcb->HCS_FirstDone = NULL; + pCurHcb->HCS_LastDone = NULL; + pCurHcb->HCS_ActScb = NULL; + pCurHcb->HCS_ActTcs = NULL; + + tul_read_eeprom( pCurHcb->HCS_Base ); + /*---------- get H/A configuration -------------*/ + if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8) + pCurHcb->HCS_MaxTar = 8; + else + pCurHcb->HCS_MaxTar = 16; + + pCurHcb->HCS_Config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1; + + pCurHcb->HCS_SCSI_ID = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID; + pCurHcb->HCS_IdMask = ~(1 << pCurHcb->HCS_SCSI_ID); + +#if CHK_PARITY + /* Enable parity error response */ + TUL_WR(pCurHcb->HCS_Base + TUL_PCMD, TUL_RD(pCurHcb->HCS_Base, TUL_PCMD) | 0x40); +#endif + + /* Mask all the interrupt */ + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + + tul_stop_bm(pCurHcb); + /* --- Initialize the tulip --- */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_RST_CHIP); + + /* program HBA's SCSI ID */ + TUL_WR(pCurHcb->HCS_Base + TUL_SScsiId, pCurHcb->HCS_SCSI_ID << 4); + + /* Enable Initiator Mode ,phase latch,alternate sync period mode, + disable SCSI reset */ + if (pCurHcb->HCS_Config & HCC_EN_PAR) + pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR); + else + pCurHcb->HCS_SConf1 = (TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurHcb->HCS_SConf1); + + /* Enable HW reselect */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT ); + + TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, 0); + + /* selection time out = 250 ms */ + TUL_WR(pCurHcb->HCS_Base + TUL_STimeOut, 153); + + /*--------- Enable SCSI terminator -----*/ + TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, (pCurHcb->HCS_Config & (HCC_ACT_TERM1 | HCC_ACT_TERM2))); + TUL_WR(pCurHcb->HCS_Base + TUL_GCTRL1, + ((pCurHcb->HCS_Config & HCC_AUTO_TERM) >> 4) | (TUL_RD(pCurHcb->HCS_Base, TUL_GCTRL1) & 0xFE)); + +// dBiosAdr += 0x180; + for (i = 0, + pwFlags = (WORD *) &(i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config), +// pbHeads = (BYTE *)(dBiosAdr); + pbHeads = pbBiosAdr+0x180; + i < pCurHcb->HCS_MaxTar; + i++, pwFlags++) + { + pCurHcb->HCS_Tcs[i].TCS_Flags = *pwFlags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE); + if (pCurHcb->HCS_Tcs[i].TCS_Flags & TCF_EN_255) + pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63; + else + pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0; + pCurHcb->HCS_Tcs[i].TCS_JS_Period = 0; + pCurHcb->HCS_Tcs[i].TCS_SConfig0 = pCurHcb->HCS_SConf1; + pCurHcb->HCS_Tcs[i].TCS_DrvHead = *pbHeads++; + if (pCurHcb->HCS_Tcs[i].TCS_DrvHead == 255) + pCurHcb->HCS_Tcs[i].TCS_DrvFlags = TCF_DRV_255_63; + else + pCurHcb->HCS_Tcs[i].TCS_DrvFlags = 0; + pCurHcb->HCS_Tcs[i].TCS_DrvSector = *pbHeads++; + pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY; + pCurHcb->HCS_ActTags[i] = 0; + pCurHcb->HCS_MaxTags[i] = 0xFF; + } /* for */ +#if 1 + printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n", + pCurHcb->HCS_Base, pCurHcb->HCS_Intr, + pCurHcb->HCS_BIOS, pCurHcb->HCS_SCSI_ID); +#endif + /*------------------- reset SCSI Bus ---------------------------*/ + if (pCurHcb->HCS_Config & HCC_SCSI_RESET) + { +#if 1 + printk("i91u: Reset SCSI Bus ... \n"); +#endif + tul_reset_scsi(pCurHcb, seconds); + } + TUL_WR(pCurHcb->HCS_Base + TUL_SCFG1, 0x17); + TUL_WR(pCurHcb->HCS_Base + TUL_SIntEnable, 0xE9); + return(0); +} + +/***************************************************************************/ +SCB *tul_alloc_scb(HCS *hcsp) +{ + SCB *pTmpScb; + ULONG flags; + + save_flags(flags); + cli(); + if ((pTmpScb = hcsp->HCS_FirstAvail) != NULL) + { +#if DEBUG_QUEUE + printk("find scb at %08lx\n", (ULONG) pTmpScb); +#endif + if ((hcsp->HCS_FirstAvail = pTmpScb->SCB_NxtScb) == NULL) + hcsp->HCS_LastAvail = NULL; + pTmpScb->SCB_NxtScb = NULL; + pTmpScb->SCB_Status = SCB_RENT; + } + restore_flags(flags); + return (pTmpScb); +} + +/***************************************************************************/ +void tul_release_scb(HCS *hcsp, SCB *scbp) +{ + ULONG flags; + +#if DEBUG_QUEUE +printk("Release SCB %lx; ", (ULONG) scbp); +#endif + save_flags(flags); + cli(); + scbp->SCB_Srb = 0; + scbp->SCB_Status = 0; + scbp->SCB_NxtScb = NULL; + if (hcsp->HCS_LastAvail != NULL) + { + hcsp->HCS_LastAvail->SCB_NxtScb = scbp; + hcsp->HCS_LastAvail = scbp; + } + else + { + hcsp->HCS_FirstAvail = scbp; + hcsp->HCS_LastAvail = scbp; + } + restore_flags(flags); +} + +/***************************************************************************/ +void tul_append_pend_scb(HCS *pCurHcb, SCB *scbp) +{ + +#if DEBUG_QUEUE +printk("Append pend SCB %lx; ", (ULONG) scbp); +#endif + scbp->SCB_Status = SCB_PEND; + scbp->SCB_NxtScb = NULL; + if (pCurHcb->HCS_LastPend != NULL) + { + pCurHcb->HCS_LastPend->SCB_NxtScb = scbp; + pCurHcb->HCS_LastPend = scbp; + } + else + { + pCurHcb->HCS_FirstPend = scbp; + pCurHcb->HCS_LastPend = scbp; + } +} + +/***************************************************************************/ +void tul_push_pend_scb(HCS *pCurHcb, SCB *scbp) +{ + +#if DEBUG_QUEUE +printk("Push pend SCB %lx; ", (ULONG) scbp); +#endif + scbp->SCB_Status = SCB_PEND; + if ((scbp->SCB_NxtScb = pCurHcb->HCS_FirstPend) != NULL) + { + pCurHcb->HCS_FirstPend = scbp; + } + else + { + pCurHcb->HCS_FirstPend = scbp; + pCurHcb->HCS_LastPend = scbp; + } +} + +/***************************************************************************/ +SCB *tul_find_first_pend_scb(HCS *pCurHcb) +{ + SCB *pFirstPend; + + + pFirstPend = pCurHcb->HCS_FirstPend; + while (pFirstPend != NULL) { + if (pFirstPend->SCB_Opcode != ExecSCSI) { + return (pFirstPend); + } + + if (pFirstPend->SCB_TagMsg == 0) + { + if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] == 0) && + !(pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) + { + return (pFirstPend); + } + } + else + { + if ((pCurHcb->HCS_ActTags[pFirstPend->SCB_Target] >= + pCurHcb->HCS_MaxTags[pFirstPend->SCB_Target]) | + (pCurHcb->HCS_Tcs[pFirstPend->SCB_Target].TCS_Flags & TCF_BUSY)) + { + pFirstPend = pFirstPend->SCB_NxtScb; + continue; + } + return (pFirstPend); + } + pFirstPend = pFirstPend->SCB_NxtScb; + } + + + return (pFirstPend); +} +/***************************************************************************/ +SCB *tul_pop_pend_scb(HCS *pCurHcb) +{ + SCB *pTmpScb; + + if ((pTmpScb = pCurHcb->HCS_FirstPend) != NULL) + { + if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastPend = NULL; + pTmpScb->SCB_NxtScb = NULL; + } +#if DEBUG_QUEUE +printk("Pop pend SCB %lx; ", (ULONG) pTmpScb); +#endif + return (pTmpScb); +} + + +/***************************************************************************/ +void tul_unlink_pend_scb(HCS *pCurHcb, SCB *pCurScb) +{ + SCB *pTmpScb, *pPrevScb; + +#if DEBUG_QUEUE +printk("unlink pend SCB %lx; ", (ULONG) pCurScb); +#endif + + pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend; + while (pTmpScb != NULL) + { + if (pCurScb == pTmpScb) + { /* Unlink this SCB */ + if (pTmpScb == pCurHcb->HCS_FirstPend) + { + if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastPend = NULL; + } + else + { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastPend) + pCurHcb->HCS_LastPend = pPrevScb; + } + pTmpScb->SCB_NxtScb = NULL; + break; + } + pPrevScb = pTmpScb; + pTmpScb = pTmpScb->SCB_NxtScb; + } + return; +} +/***************************************************************************/ +void tul_append_busy_scb(HCS *pCurHcb, SCB *scbp) +{ + +#if DEBUG_QUEUE +printk("append busy SCB %lx; ", (ULONG) scbp); +#endif + if (scbp->SCB_TagMsg) + pCurHcb->HCS_ActTags[scbp->SCB_Target]++; + else + pCurHcb->HCS_Tcs[scbp->SCB_Target].TCS_Flags |= TCF_BUSY; + scbp->SCB_Status = SCB_BUSY; + scbp->SCB_NxtScb = NULL; + if (pCurHcb->HCS_LastBusy != NULL) + { + pCurHcb->HCS_LastBusy->SCB_NxtScb = scbp; + pCurHcb->HCS_LastBusy = scbp; + } + else + { + pCurHcb->HCS_FirstBusy = scbp; + pCurHcb->HCS_LastBusy = scbp; + } +} + +/***************************************************************************/ +SCB *tul_pop_busy_scb(HCS *pCurHcb) +{ + SCB *pTmpScb; + + + if ((pTmpScb = pCurHcb->HCS_FirstBusy) != NULL) + { + if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastBusy = NULL; + pTmpScb->SCB_NxtScb = NULL; + if (pTmpScb->SCB_TagMsg) + pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--; + else + pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY; + } +#if DEBUG_QUEUE +printk("Pop busy SCB %lx; ", (ULONG) pTmpScb); +#endif + return (pTmpScb); +} + +/***************************************************************************/ +void tul_unlink_busy_scb(HCS *pCurHcb, SCB *pCurScb) +{ + SCB *pTmpScb, *pPrevScb; + +#if DEBUG_QUEUE +printk("unlink busy SCB %lx; ", (ULONG) pCurScb); +#endif + + pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; + while (pTmpScb != NULL) + { + if (pCurScb == pTmpScb) + { /* Unlink this SCB */ + if (pTmpScb == pCurHcb->HCS_FirstBusy) + { + if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastBusy = NULL; + } + else + { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastBusy) + pCurHcb->HCS_LastBusy = pPrevScb; + } + pTmpScb->SCB_NxtScb = NULL; + if (pTmpScb->SCB_TagMsg) + pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--; + else + pCurHcb->HCS_Tcs[pTmpScb->SCB_Target].TCS_Flags &= ~TCF_BUSY; + break; + } + pPrevScb = pTmpScb; + pTmpScb = pTmpScb->SCB_NxtScb; + } + return; +} + +/***************************************************************************/ +SCB *tul_find_busy_scb(HCS *pCurHcb, WORD tarlun) +{ + SCB *pTmpScb, *pPrevScb; + WORD scbp_tarlun; + + + pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; + while (pTmpScb != NULL) + { + scbp_tarlun = (pTmpScb->SCB_Lun << 8) | (pTmpScb->SCB_Target); + if (scbp_tarlun == tarlun) + { /* Unlink this SCB */ +#if 0 + if (pTmpScb == pCurHcb->HCS_FirstBusy) + { + if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastBusy = NULL; + } + else + { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastBusy) + pCurHcb->HCS_LastBusy = pPrevScb; + } + pTmpScb->SCB_NxtScb = NULL; +#endif + break; + } + pPrevScb = pTmpScb; + pTmpScb = pTmpScb->SCB_NxtScb; + } +#if DEBUG_QUEUE +printk("find busy SCB %lx; ", (ULONG) pTmpScb); +#endif + return (pTmpScb); +} + +/***************************************************************************/ +void tul_append_done_scb(HCS *pCurHcb, SCB *scbp) +{ + +#if DEBUG_QUEUE +printk("append done SCB %lx; ", (ULONG) scbp); +#endif +/* tul_unlink_busy_scb(pCurHcb, scbp);*/ + + scbp->SCB_Status = SCB_DONE; + scbp->SCB_NxtScb = NULL; + if (pCurHcb->HCS_LastDone != NULL) + { + pCurHcb->HCS_LastDone->SCB_NxtScb = scbp; + pCurHcb->HCS_LastDone = scbp; + } + else + { + pCurHcb->HCS_FirstDone = scbp; + pCurHcb->HCS_LastDone = scbp; + } +} + +/***************************************************************************/ +SCB *tul_find_done_scb(HCS *pCurHcb) +{ + SCB *pTmpScb; + + + if ((pTmpScb = pCurHcb->HCS_FirstDone) != NULL) + { + if ((pCurHcb->HCS_FirstDone = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastDone = NULL; + pTmpScb->SCB_NxtScb = NULL; + } +#if DEBUG_QUEUE +printk("find done SCB %lx; ", (ULONG) pTmpScb); +#endif + return (pTmpScb); +} + +/***************************************************************************/ +int tul_abort_srb(HCS *pCurHcb, ULONG srbp) +{ + ULONG flags; + SCB *pTmpScb, *pPrevScb; + + save_flags(flags); + cli(); + + if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) { + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + /* disable Jasmin SCSI Int */ + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + + restore_flags(flags); + + return SCSI_ABORT_SNOOZE; + } + + pPrevScb = pTmpScb = pCurHcb->HCS_FirstPend; /* Check Pend queue */ + while (pTmpScb != NULL) + { + /* 07/27/98 */ + if (pTmpScb->SCB_Srb == (unsigned char *)srbp) { + if (pTmpScb == pCurHcb->HCS_ActScb) { + restore_flags(flags); + return SCSI_ABORT_BUSY; + } + else if (pTmpScb == pCurHcb->HCS_FirstPend) { + if ((pCurHcb->HCS_FirstPend = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastPend = NULL; + } + else { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastPend) + pCurHcb->HCS_LastPend = pPrevScb; + } + pTmpScb->SCB_HaStat = HOST_ABORTED; + pTmpScb->SCB_Flags |= SCF_DONE; + if (pTmpScb->SCB_Flags & SCF_POST) + (*pTmpScb->SCB_Post)((BYTE *) pCurHcb, (BYTE *) pTmpScb); + restore_flags(flags); + return SCSI_ABORT_SUCCESS ; + } + pPrevScb = pTmpScb; + pTmpScb = pTmpScb->SCB_NxtScb; + } + + pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */ + while (pTmpScb != NULL) { + + if (pTmpScb->SCB_Srb == (unsigned char *)srbp) { + + if (pTmpScb == pCurHcb->HCS_ActScb) { + restore_flags(flags); + return SCSI_ABORT_BUSY; + } + else if (pTmpScb->SCB_TagMsg == 0) { + restore_flags(flags); + return SCSI_ABORT_BUSY; + } + else { + pCurHcb->HCS_ActTags[pTmpScb->SCB_Target]--; + if (pTmpScb == pCurHcb->HCS_FirstBusy) { + if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastBusy = NULL; + } + else { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastBusy) + pCurHcb->HCS_LastBusy = pPrevScb; + } + pTmpScb->SCB_NxtScb = NULL; + + + pTmpScb->SCB_HaStat = HOST_ABORTED; + pTmpScb->SCB_Flags |= SCF_DONE; + if (pTmpScb->SCB_Flags & SCF_POST) + (*pTmpScb->SCB_Post)((BYTE *) pCurHcb, (BYTE *) pTmpScb); + restore_flags(flags); + return SCSI_ABORT_SUCCESS ; + } + } + pPrevScb = pTmpScb; + pTmpScb = pTmpScb->SCB_NxtScb; + } + restore_flags(flags); + return (SCSI_ABORT_NOT_RUNNING); +} + +/***************************************************************************/ +int tul_bad_seq(HCS *pCurHcb) +{ + SCB *pCurScb; + +#if 1 +printk("tul_bad_seg c=%d\n", pCurHcb->HCS_Index); +#endif + if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) + { + tul_unlink_busy_scb(pCurHcb, pCurScb); + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + pCurScb->SCB_TaStat = 0; +// pCurScb->SCB_Status = SCB_DONE; /* Done */ + tul_append_done_scb(pCurHcb, pCurScb); + } + + tul_stop_bm(pCurHcb); + + tul_reset_scsi(pCurHcb, 8); /* 7/29/98 */ + + return (tul_post_scsi_rst(pCurHcb)); +} + +/************************************************************************/ +int tul_device_reset(HCS *pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags) +{ + ULONG flags; + SCB *pScb; + +#if 0 +printk("tul_device_reset c=%d %d \n", pCurHcb->HCS_Index, pCurHcb->HCS_Semaph); +#endif + + save_flags(flags); + cli(); + + + if (ResetFlags & SCSI_RESET_ASYNCHRONOUS) { + + if ((pCurHcb->HCS_Semaph == 0) && (pCurHcb->HCS_ActScb == NULL)) { + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + /* disable Jasmin SCSI Int */ + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + + restore_flags(flags); + + return SCSI_RESET_SNOOZE ; + } + + pScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */ + while (pScb != NULL) { + if (pScb->SCB_Srb == (unsigned char *)pSrb) + break; + pScb = pScb->SCB_NxtScb; + } + if (pScb == NULL) { + printk("Unable to Reset - No SCB Found\n"); + + restore_flags(flags); + return SCSI_RESET_NOT_RUNNING; + } + } + if ((pScb = tul_alloc_scb(pCurHcb)) == NULL) { + restore_flags(flags); + return SCSI_RESET_NOT_RUNNING; + } + pScb->SCB_Opcode = BusDevRst; + pScb->SCB_Flags = SCF_POST; + pScb->SCB_Target = target; + pScb->SCB_Mode = 0; + + pScb->SCB_Srb = 0; + if (ResetFlags & SCSI_RESET_SYNCHRONOUS) + { + pScb->SCB_Srb = (unsigned char *)pSrb; + } + tul_push_pend_scb(pCurHcb,pScb); /* push this SCB to Pending queue */ + + if (pCurHcb->HCS_Semaph == 1) { + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + /* disable Jasmin SCSI Int */ + pCurHcb->HCS_Semaph = 0; + + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + } + restore_flags(flags); + return SCSI_RESET_PENDING; + + +} + +int tul_reset_scsi_bus(HCS *pCurHcb) +{ + ULONG flags; + +#if 0 +printk("tul_reset_scsi_bus c=%d %d \n", pCurHcb->HCS_Index, pCurHcb->HCS_Semaph); +#endif + + save_flags(flags); + cli(); + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + pCurHcb->HCS_Semaph = 0; + + restore_flags(flags); + + tul_stop_bm(pCurHcb); + + tul_reset_scsi(pCurHcb, 2); /* 7/29/98 */ + + save_flags(flags); + cli(); + tul_post_scsi_rst(pCurHcb); + +// tul_do_pause(7*100); + + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + restore_flags(flags); + return (SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET); +} + +/************************************************************************/ +void tul_exec_scb(HCS *pCurHcb, SCB *pCurScb) +{ + ULONG flags; + +/* printk("Enter tul_exec_scb");*/ + + + +/* pCurScb->SCB_Status = SCB_PEND; 7/22/98 */ + pCurScb->SCB_Mode = 0; + + pCurScb->SCB_SGIdx = 0; + pCurScb->SCB_SGMax = pCurScb->SCB_SGLen; + + save_flags(flags); + cli(); + + tul_append_pend_scb(pCurHcb,pCurScb); /* Append this SCB to Pending queue */ + +/* VVVVV 07/21/98 */ + if (pCurHcb->HCS_Semaph == 1) { + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + /* disable Jasmin SCSI Int */ + pCurHcb->HCS_Semaph = 0; + + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + } + restore_flags(flags); + return; +} + +/***************************************************************************/ +int tul_isr(HCS *pCurHcb) +{ +/* printk("Enter i91u_isr\n");*/ + /* Enter critical section */ + + if (TUL_RD(pCurHcb->HCS_Base, TUL_Int) & TSS_INT_PENDING) + { + if (pCurHcb->HCS_Semaph == 1) + { + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x1F); + /* Disable Tulip SCSI Int */ + pCurHcb->HCS_Semaph = 0; + + tulip_main(pCurHcb); + + pCurHcb->HCS_Semaph = 1; + TUL_WR(pCurHcb->HCS_Base + TUL_Mask, 0x0F); + return (1); + } + } + return (0); +} + +/***************************************************************************/ +int tulip_main(HCS *pCurHcb) +{ + SCB *pCurScb; +// ULONG flags; + +/*printk("Enter tulip_main\n");*/ + + for (;;) + { + +// save_flags(flags); /* wh 07/21/98 */ +// sti(); + + tulip_scsi(pCurHcb); /* Call tulip_scsi */ + +// restore_flags(flags); /* Addedby hc 07/21/98 */ + + while ((pCurScb = tul_find_done_scb(pCurHcb)) != NULL) + {/* find done entry */ + if (pCurScb->SCB_TaStat == QUEUE_FULL) + { + pCurHcb->HCS_MaxTags[pCurScb->SCB_Target] = + pCurHcb->HCS_ActTags[pCurScb->SCB_Target] - 1; + pCurScb->SCB_TaStat = 0; +// pCurScb->SCB_Status = SCB_PEND; + tul_append_pend_scb(pCurHcb, pCurScb); + continue; + } + + if (!(pCurScb->SCB_Mode & SCM_RSENS)) + { /* not in auto req. sense mode*/ + if (pCurScb->SCB_TaStat == 2) + { + +// pCurHcb->HCS_Tcs[pCurScb->SCB_Target].TCS_Flags +// &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); + /* clr sync. nego flag */ + + if (pCurScb->SCB_Flags & SCF_SENSE) + { + BYTE len; + len = pCurScb->SCB_SenseLen; + if (len == 0) + len = 1; + pCurScb->SCB_BufLen = pCurScb->SCB_SenseLen; + pCurScb->SCB_BufPtr = pCurScb->SCB_SensePtr; + pCurScb->SCB_Flags &= ~(SCF_SG|SCF_DIR); /* for xfer_data_in */ +/* pCurScb->SCB_Flags |= SCF_NO_DCHK; */ + /* so, we won't report worng direction in xfer_data_in, + and won't report HOST_DO_DU in state_6 */ + pCurScb->SCB_Mode = SCM_RSENS; + pCurScb->SCB_Ident &= 0xBF; /* Disable Disconnect */ + pCurScb->SCB_TagMsg = 0; + pCurScb->SCB_TaStat = 0; + pCurScb->SCB_CDBLen = 6; + pCurScb->SCB_CDB[0] = SCSICMD_RequestSense; + pCurScb->SCB_CDB[1] = 0; + pCurScb->SCB_CDB[2] = 0; + pCurScb->SCB_CDB[3] = 0; + pCurScb->SCB_CDB[4] = len; + pCurScb->SCB_CDB[5] = 0; + tul_push_pend_scb(pCurHcb, pCurScb); + break; + } + } + } + else { /* in request sense mode */ + + if (pCurScb->SCB_TaStat == 2) + {/* check contition status again after sending + requset sense cmd 0x3*/ + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + } + pCurScb->SCB_TaStat = 2; + } + pCurScb->SCB_Flags |= SCF_DONE; + if (pCurScb->SCB_Flags & SCF_POST) + { +// restore_flags(flags); /* Addedby hc 07/08/98 */ +// sti(); + (*pCurScb->SCB_Post)((BYTE *) pCurHcb, (BYTE *) pCurScb); +// save_flags(flags); /* Addedby hc 07/08/98 */ + } + } /* while */ + + /* find_active: */ + if (TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0) & TSS_INT_PENDING) + continue; + + if (pCurHcb->HCS_ActScb) /* return to OS and wait for xfer_done_ISR/Selected_ISR */ + { + return 1; /* return to OS, enable interrupt */ + } + /* Check pending SCB */ + if (tul_find_first_pend_scb(pCurHcb) == NULL) + { + return 1; /* return to OS, enable interrupt */ + } + } /* End of for loop */ + /* statement won't reach here */ +} + + + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ +/***************************************************************************/ + +/***************************************************************************/ +void tulip_scsi(HCS *pCurHcb) +{ + SCB *pCurScb; + TCS *pCurTcb; + +/* printk("Enter tulip_scsi()\n");*/ + /* make sure to service interrupt asap */ + + if ((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus0)) & TSS_INT_PENDING) + { + + pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK; + pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1); + pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt); +/* printk("SCSI status = %x\n", pCurHcb->HCS_JSStatus0 ); + printk("SCSI status 1 = %x\n", pCurHcb->HCS_JSStatus1); + printk("SCSI interrupt = %x\n", pCurHcb->HCS_JSInt);*/ + if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) + { /* SCSI bus reset detected */ + int_tul_scsi_rst(pCurHcb); + return; + } + if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) + { /* if selected/reselected interrupt */ + if (int_tul_resel(pCurHcb) == 0) + tul_next_state(pCurHcb); + return; + } + if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) + { + int_tul_busfree(pCurHcb) ; + return; + } + if (pCurHcb->HCS_JSInt & TSS_DISC_INT) + { /* BUS disconnection */ + int_tul_busfree(pCurHcb); /* unexpected bus free or sel timeout */ + return; + } + if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) + { /* func complete or Bus service */ + if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) + tul_next_state(pCurHcb); + return; + } + } + + if (pCurHcb->HCS_ActScb != NULL) + return; + +/* if ((pCurScb = pCurHcb->HCS_FirstPend) == NULL)*/ + if ((pCurScb = tul_find_first_pend_scb(pCurHcb)) == NULL) + return; + + /* program HBA's SCSI ID & target SCSI ID */ + TUL_WR( pCurHcb->HCS_Base + TUL_SScsiId, + (pCurHcb->HCS_SCSI_ID << 4) | (pCurScb->SCB_Target & 0x0F) ); + if (pCurScb->SCB_Opcode == ExecSCSI) { + pCurTcb = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target]; + + if (pCurScb->SCB_TagMsg) + pCurTcb->TCS_DrvFlags |= TCF_DRV_EN_TAG; + else + pCurTcb->TCS_DrvFlags &= ~TCF_DRV_EN_TAG; + + TUL_WR(pCurHcb->HCS_Base + TUL_SPeriod, pCurTcb->TCS_JS_Period); +/*printk("TCS_Flags = %x\n", pCurTcb->TCS_Flags); +do_pause(15000);*/ + if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) + { /* do wdtr negotiation */ + tul_select_atn_stop(pCurHcb, pCurScb); + } + else + { + if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) + { /* do sync negotiation */ + tul_select_atn_stop(pCurHcb, pCurScb); + } + else + { + if (pCurScb->SCB_TagMsg) + tul_select_atn3(pCurHcb, pCurScb); + else + tul_select_atn(pCurHcb, pCurScb); + } + } + if (pCurScb->SCB_Flags & SCF_POLL) + { + while (wait_tulip(pCurHcb) != -1) + { + if (tul_next_state(pCurHcb) == -1) + break; + } + } + } + else if (pCurScb->SCB_Opcode == BusDevRst) { + tul_select_atn_stop(pCurHcb, pCurScb); + pCurScb->SCB_NxtStat = 8; + if (pCurScb->SCB_Flags & SCF_POLL) + { + while (wait_tulip(pCurHcb) != -1) + { + if (tul_next_state(pCurHcb) == -1) + break; + } + } + } + else if (pCurScb->SCB_Opcode == AbortCmd ) { + ULONG srbp; + + srbp = (ULONG)pCurScb->SCB_Srb; +/* 08/03/98 */ + if (tul_abort_srb(pCurHcb, srbp) != 0) { + + + tul_unlink_pend_scb(pCurHcb, pCurScb); + + tul_release_scb(pCurHcb, pCurScb); + } + else { + pCurScb->SCB_Opcode = BusDevRst; + tul_select_atn_stop(pCurHcb, pCurScb); + pCurScb->SCB_NxtStat = 8; + } + +/* 08/03/98 */ + } + else { + tul_unlink_pend_scb(pCurHcb, pCurScb); +// pCurScb->SCB_Status = SCB_DONE; /* done */ + pCurScb->SCB_HaStat = 0x16; /* bad command */ + tul_append_done_scb(pCurHcb, pCurScb); + } + return; +} + + +/***************************************************************************/ +int tul_next_state(HCS *pCurHcb) +{ + int next; + + next = pCurHcb->HCS_ActScb->SCB_NxtStat; + for (;;) { + switch (next) { + case 1: + next = tul_state_1(pCurHcb); + break; + case 2: + next = tul_state_2(pCurHcb); + break; + case 3: + next = tul_state_3(pCurHcb); + break; + case 4: + next = tul_state_4(pCurHcb); + break; + case 5: + next = tul_state_5(pCurHcb); + break; + case 6: + next = tul_state_6(pCurHcb); + break; + case 7: + next = tul_state_7(pCurHcb); + break; + case 8: + return (tul_bus_device_reset(pCurHcb)); + default: + return (tul_bad_seq(pCurHcb)); + } + if (next <= 0) + return next; + } +} + + +/***************************************************************************/ +/* sTate after selection with attention & stop */ +int tul_state_1(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; +#if DEBUG_STATE +printk("-s1-"); +#endif + + tul_unlink_pend_scb(pCurHcb, pCurScb); + tul_append_busy_scb(pCurHcb, pCurScb); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SConfig, pCurTcb->TCS_SConfig0); + /* ATN on */ + if (pCurHcb->HCS_Phase == MSG_OUT) { + +/*printk("MSG OUT\n");*/ + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl1, (TSC_EN_BUS_IN | TSC_HW_RESELECT)); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_Ident); + + if (pCurScb->SCB_TagMsg) + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_TagMsg); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_TagId); + } + + if ((pCurTcb->TCS_Flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { + + pCurTcb->TCS_Flags |= TCF_WDTR_DONE; + + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_EXTEND); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 2); /* Extended msg length*/ + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 3); /* Sync request*/ + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 1); /* Start from 16 bits */ + } + else if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE|TCF_NO_SYNC_NEGO)) == 0) { + + pCurTcb->TCS_Flags |= TCF_SYNC_DONE; + + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MSG_EXTEND); + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, 3); /* extended msg length */ + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, 1); /* sync request */ + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]); + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */ + } + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return(-1); + } + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7))); + return(3); +} + + +/***************************************************************************/ +/* state after selection with attention */ +/* state after selection with attention3 */ +int tul_state_2(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; +#if DEBUG_STATE +printk("-s2-"); +#endif + + tul_unlink_pend_scb(pCurHcb, pCurScb); + tul_append_busy_scb(pCurHcb, pCurScb); + + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0); + + if ( pCurHcb->HCS_JSStatus1 & TSS_CMD_PH_CMP) { + return (4); + } + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7))); + return (3); +} + +/***************************************************************************/ +/* state before CDB xfer is done */ +int tul_state_3(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; + int i; + +#if DEBUG_STATE +printk("-s3-"); +#endif + for (;;) { + switch (pCurHcb->HCS_Phase) + { + case CMD_OUT: /* Command out phase */ +/*printk("Command Out Phase\n");*/ + for ( i = 0; i < (int) pCurScb->SCB_CDBLen; i++) + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return (-1); + if (pCurHcb->HCS_Phase == CMD_OUT) { + return (tul_bad_seq(pCurHcb)); + } + return(4); + + case MSG_IN: /* Message in phase */ +/*printk("Message In Phase\n");*/ + pCurScb->SCB_NxtStat = 3; + if (tul_msgin(pCurHcb) == -1) + return (-1); + break; + + case STATUS_IN: /* Status phase */ +/*printk("Status Phase\n");*/ + if (tul_status_msg(pCurHcb) == -1) + return (-1); + break; + + case MSG_OUT: /* Message out phase */ +/*printk("Message Out Phase\n");*/ + if (pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) { + + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MSG_NOP); /* msg nop */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return(-1); + + } else { + pCurTcb->TCS_Flags |= TCF_SYNC_DONE; + + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MSG_EXTEND); + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, 3); /* ext. msg len */ + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, 1); /* sync request */ + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, tul_rate_tbl[pCurTcb->TCS_Flags & TCF_SCSI_RATE]); + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MAX_OFFSET); /* REQ/ACK offset */ + TUL_WR(pCurHcb->HCS_Base+TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return(-1); + TUL_WR(pCurHcb->HCS_Base+TUL_SCtrl0, TSC_FLUSH_FIFO); + TUL_WR(pCurHcb->HCS_Base+TUL_SSignal, TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)); + + } + break; + + default: + return (tul_bad_seq(pCurHcb)); + } + } +} + + +/***************************************************************************/ +int tul_state_4(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + +#if DEBUG_STATE +printk("-s4-"); +#endif + if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_NO_XF) { + return (6); /* Go to state 6 */ + } + + for (;;) { + if (pCurScb->SCB_BufLen == 0) + return (6); /* Go to state 6 */ + + switch (pCurHcb->HCS_Phase) { + + case STATUS_IN: /* Status phase */ + if ((pCurScb->SCB_Flags & SCF_DIR) != 0) + { /* if direction bit set then report data underrun */ + pCurScb->SCB_HaStat = HOST_DO_DU; + } + if ((tul_status_msg(pCurHcb)) == -1) + return (-1); + break; + + case MSG_IN: /* Message in phase */ + pCurScb->SCB_NxtStat = 0x4; + if (tul_msgin(pCurHcb) == -1) + return (-1); + break; + + case MSG_OUT: /* Message out phase */ + if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) { + pCurScb->SCB_BufLen = 0; + pCurScb->SCB_HaStat = HOST_DO_DU; + if (tul_msgout_ide(pCurHcb) == -1) + return (-1); + return (6); /* Go to state 6 */ + } else { + TUL_WR(pCurHcb->HCS_Base+TUL_SFifo, MSG_NOP); /* msg nop */ + TUL_WR(pCurHcb->HCS_Base+TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return (-1); + } + break; + + case DATA_IN: /* Data in phase */ + return (tul_xfer_data_in(pCurHcb)); + + case DATA_OUT: /* Data out phase */ + return (tul_xfer_data_out(pCurHcb)); + + default: + return (tul_bad_seq(pCurHcb)); + } + } +} + + +/***************************************************************************/ +/* state after dma xfer done or phase change before xfer done */ +int tul_state_5(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + long cnt, xcnt; /* cannot use unsigned !! code: if (xcnt < 0) */ + +#if DEBUG_STATE +printk("-s5-"); +#endif + /*------ get remaining count -------*/ + + cnt = TUL_RDLONG(pCurHcb->HCS_Base, TUL_SCnt0) & 0x0FFFFFF; + +/*printk("Remaining SCSI Count = %lx\n", cnt);*/ + if (TUL_RD(pCurHcb->HCS_Base, TUL_XCmd) & 0x20) + { + /* ----------------------- DATA_IN -----------------------------*/ + /* check scsi parity error */ + if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) + { + pCurScb->SCB_HaStat = HOST_DO_DU; + } + + if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) + { /* DMA xfer pending, Send STOP */ + /* tell Hardware scsi xfer has been terminated */ + TUL_WR(pCurHcb->HCS_Base + TUL_XCtrl, TUL_RD(pCurHcb->HCS_Base, TUL_XCtrl) | 0x80); + /* wait until DMA xfer not pending */ + while (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND ); + } + } + else + { /*-------- DATA OUT -----------*/ + if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) + { + if (pCurHcb->HCS_ActTcs->TCS_JS_Period & TSC_WIDE_SCSI) + cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F) << 1; + else + cnt += (TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F); + } + if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & XPEND) + { /* if DMA xfer is pending, abort DMA xfer */ + TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT); + /* wait Abort DMA xfer done */ + while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & XABT) == 0); + } + if ((cnt == 1) && (pCurHcb->HCS_Phase == DATA_OUT)) + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + { + return (-1); + } + cnt = 0; + } + else + { + if ((TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1) & TSS_XFER_CMP) == 0) + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + } + } + + if (cnt == 0) + { + pCurScb->SCB_BufLen = 0; + return (6); /* Go to state 6 */ + } + /* Update active data pointer */ + xcnt = (long)pCurScb->SCB_BufLen - cnt; /* xcnt== bytes already xferred */ + pCurScb->SCB_BufLen = (U32)cnt; /* cnt == bytes left to be xferred */ + if (pCurScb->SCB_Flags & SCF_SG) + { + register SG *sgp; + ULONG i; + + sgp = &pCurScb->SCB_SGList[pCurScb->SCB_SGIdx]; + for (i = pCurScb->SCB_SGIdx; i < pCurScb->SCB_SGMax; sgp++, i++) + { + xcnt -= (long)sgp->SG_Len; + if (xcnt < 0) + { /* this sgp xfer half done */ + xcnt += (long)sgp->SG_Len; /* xcnt == bytes xferred in this sgp */ + sgp->SG_Ptr += (U32)xcnt; /* new ptr to be xfer */ + sgp->SG_Len -= (U32)xcnt; /* new len to be xfer */ + pCurScb->SCB_BufPtr += ((U32)(i - pCurScb->SCB_SGIdx) << 3); + /* new SG table ptr */ + pCurScb->SCB_SGLen = (BYTE)(pCurScb->SCB_SGMax - i); + /* new SG table len */ + pCurScb->SCB_SGIdx = (WORD)i; + /* for next disc and come in this loop*/ + return (4); /* Go to state 4 */ + } + /* else (xcnt >= 0 , i.e. this sgp already xferred */ + } /* for */ + return (6); /* Go to state 6 */ + } + else + { + pCurScb->SCB_BufPtr += (U32)xcnt; + } + return (4); /* Go to state 4 */ +} + +/***************************************************************************/ +/* state after Data phase */ +int tul_state_6(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + +#if DEBUG_STATE +printk("-s6-"); +#endif + for (;;) { + switch (pCurHcb->HCS_Phase) { + case STATUS_IN: /* Status phase */ + if ((tul_status_msg(pCurHcb)) == -1) + return (-1); + break; + + case MSG_IN: /* Message in phase */ + pCurScb->SCB_NxtStat = 6; + if ((tul_msgin(pCurHcb)) == -1) + return (-1); + break; + + case MSG_OUT: /* Message out phase */ + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_NOP); /* msg nop */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + if (wait_tulip(pCurHcb) == -1) + return (-1); + break; + + case DATA_IN: /* Data in phase */ + return (tul_xpad_in(pCurHcb)); + + case DATA_OUT: /* Data out phase */ + return (tul_xpad_out(pCurHcb)); + + default: + return (tul_bad_seq(pCurHcb)); + } + } +} + +/***************************************************************************/ +int tul_state_7(HCS *pCurHcb) +{ + int cnt, i; + +#if DEBUG_STATE +printk("-s7-"); +#endif + /* flush SCSI FIFO */ + cnt = TUL_RD(pCurHcb->HCS_Base, TUL_SFifoCnt) & 0x1F; + if (cnt) { + for (i = 0; i < cnt; i++) + TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + } + + switch (pCurHcb->HCS_Phase) { + case DATA_IN: /* Data in phase */ + case DATA_OUT: /* Data out phase */ + return (tul_bad_seq(pCurHcb)); + default: + return (6); /* Go to state 6 */ + } +} + +/***************************************************************************/ +int tul_xfer_data_in(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + +/*printk("\nEnter xfer data in; ptr = %lx; cnt = %lx\n", pCurScb->SCB_BufPtr, + pCurScb->SCB_BufLen);*/ + if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DOUT) { + return (6); /* wrong direction */ + } + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, pCurScb->SCB_BufLen); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_DMA_IN); /* 7/25/95 */ + + if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */ + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XCntH, ((ULONG)pCurScb->SCB_SGLen) << 3); + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XAddH, pCurScb->SCB_BufPtr); + TUL_WR(pCurHcb->HCS_Base+ TUL_XCmd, TAX_SG_IN); + } else { + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XCntH, pCurScb->SCB_BufLen); + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XAddH, pCurScb->SCB_BufPtr); + TUL_WR(pCurHcb->HCS_Base+ TUL_XCmd, TAX_X_IN); + } + pCurScb->SCB_NxtStat = 0x5; + return (0); /* return to OS, wait xfer done , let jas_isr come in */ +} + + +/***************************************************************************/ +int tul_xfer_data_out(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + +/*printk("Enter xfer data out\n");*/ + if ((pCurScb->SCB_Flags & SCF_DIR) == SCF_DIN) { + return (6); /* wrong direction */ + } + + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, pCurScb->SCB_BufLen); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_DMA_OUT); + + if (pCurScb->SCB_Flags & SCF_SG) { /* S/G xfer */ + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XCntH, ((ULONG)pCurScb->SCB_SGLen) << 3); + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XAddH, pCurScb->SCB_BufPtr); + TUL_WR(pCurHcb->HCS_Base+ TUL_XCmd, TAX_SG_OUT); + } else { + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XCntH, pCurScb->SCB_BufLen); + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_XAddH, pCurScb->SCB_BufPtr); + TUL_WR(pCurHcb->HCS_Base+ TUL_XCmd, TAX_X_OUT); + } + + pCurScb->SCB_NxtStat = 0x5; + return (0); /* return to OS, wait xfer done , let jas_isr come in */ +} + + +/***************************************************************************/ +int tul_xpad_in(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; + + if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) { + pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */ + } + for (;;) { + if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI) + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, 2); + else + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, 1); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_IN); + if ((wait_tulip(pCurHcb)) == -1) + { + return (-1); + } + if (pCurHcb->HCS_Phase != DATA_IN) + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + return (6); + } + TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + } +} + +int tul_xpad_out(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; + + if ((pCurScb->SCB_Flags & SCF_DIR) != SCF_NO_DCHK) { + pCurScb->SCB_HaStat = HOST_DO_DU; /* over run */ + } + for (;;) { + if (pCurTcb->TCS_JS_Period & TSC_WIDE_SCSI) + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, 2); + else + TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 0); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + if ((wait_tulip(pCurHcb)) == -1) + { + return (-1); + } + if (pCurHcb->HCS_Phase != DATA_OUT) + { /* Disable wide CPU to allow read 16 bits */ + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl1, TSC_HW_RESELECT); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + return (6); + } + } +} + + +/***************************************************************************/ +int tul_status_msg(HCS *pCurHcb) /* status & MSG_IN */ +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + BYTE msg; + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_CMD_COMP); + if ((wait_tulip(pCurHcb)) == -1) { + return (-1); + } + /* get status */ + pCurScb->SCB_TaStat = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + + if (pCurHcb->HCS_Phase == MSG_OUT) + { + if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_PARITY); + } + else + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_NOP); + } + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); + } + if (pCurHcb->HCS_Phase == MSG_IN) + { + msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + if (pCurHcb->HCS_JSStatus0 & TSS_PAR_ERROR) + { /* Parity error */ + if ((tul_msgin_accept(pCurHcb)) == -1) + return (-1); + if (pCurHcb->HCS_Phase != MSG_OUT) + return (tul_bad_seq(pCurHcb)); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_PARITY); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); + } + if (msg == 0) { /* Command complete */ + + if ((pCurScb->SCB_TaStat & 0x18) == 0x10) + { /* No link support */ + return (tul_bad_seq(pCurHcb)); + } + + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_MSG_ACCEPT); + return tul_wait_done_disc(pCurHcb); + + } + if ((msg == MSG_LINK_COMP) || (msg == MSG_LINK_FLAG)) + { + if ((pCurScb->SCB_TaStat & 0x18) == 0x10) + return (tul_msgin_accept(pCurHcb)); + } + } + return (tul_bad_seq(pCurHcb)); +} + + +/***************************************************************************/ +/* scsi bus free */ +int int_tul_busfree(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + + if (pCurScb != NULL) + { + if (pCurScb->SCB_Status & SCB_SELECT) + { /* selection timeout */ +#if 0 +// if (pCurScb != tul_pop_pend_scb(pCurHcb)) +// { +// printk("Differnet SCB\n"); +// } +#endif + tul_unlink_pend_scb(pCurHcb, pCurScb); +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_SEL_TOUT; + tul_append_done_scb(pCurHcb, pCurScb); + } + else + { /* Unexpected bus free */ + tul_unlink_busy_scb(pCurHcb, pCurScb); +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_BUS_FREE; + tul_append_done_scb(pCurHcb, pCurScb); + } + pCurHcb->HCS_ActScb = NULL; + pCurHcb->HCS_ActTcs = NULL; + } + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */ + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);/* Enable HW reselect */ + return (-1); +/* return (1);*/ +} + + +/***************************************************************************/ +/* scsi bus reset */ +int int_tul_scsi_rst(HCS *pCurHcb) +{ + SCB *pCurScb; + int i; + + /* if DMA xfer is pending, abort DMA xfer */ + if (TUL_RD(pCurHcb->HCS_Base, TUL_XStatus) & 0x01) + { + TUL_WR(pCurHcb->HCS_Base + TUL_XCmd, TAX_X_ABT | TAX_X_CLR_FIFO); + /* wait Abort DMA xfer done */ + while ((TUL_RD(pCurHcb->HCS_Base, TUL_Int) & 0x04) == 0) + ; + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); + } + /* Abort all active & disconnected scb */ + while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) + { +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + tul_append_done_scb(pCurHcb, pCurScb); + } + pCurHcb->HCS_ActScb = NULL; + pCurHcb->HCS_ActTcs = NULL; + + /* clr sync nego. done flag */ + for (i = 0; i < pCurHcb->HCS_MaxTar; i++) + { + pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); + } + return(-1); +} + + +/***************************************************************************/ +/* scsi reselection */ +int int_tul_resel(HCS *pCurHcb) +{ + SCB *pCurScb; + TCS *pCurTcb; + BYTE tag, msg = 0; + BYTE tar, lun; + + if ((pCurScb = pCurHcb->HCS_ActScb) != NULL) + { + if (pCurScb->SCB_Status & SCB_SELECT) + { /* if waiting for selection complete*/ + pCurScb->SCB_Status &= ~SCB_SELECT; + } + pCurHcb->HCS_ActScb = NULL; + } + + /* --------- get target id---------------------- */ + tar = TUL_RD(pCurHcb->HCS_Base, TUL_SBusId); + /* ------ get LUN from Identify message----------- */ + lun = TUL_RD(pCurHcb->HCS_Base, TUL_SIdent) & 0x0F; + /* 07/22/98 from 0x1F -> 0x0F */ + pCurTcb = &pCurHcb->HCS_Tcs[tar]; + pCurHcb->HCS_ActTcs = pCurTcb; + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, pCurTcb->TCS_SConfig0); + TUL_WR(pCurHcb->HCS_Base+ TUL_SPeriod, pCurTcb->TCS_JS_Period); + + + /* ------------- tag queueing ? ------------------- */ + if (pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG) + { +/*printk("Tag enabled\n");*/ + if ((tul_msgin_accept(pCurHcb)) == -1) + return (-1); + if (pCurHcb->HCS_Phase != MSG_IN) + goto no_tag; + TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_IN); + if ((wait_tulip(pCurHcb)) == -1) + return (-1); + msg = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag Message */ + + if ((msg < MSG_STAG) || (msg > MSG_OTAG)) /* Is simple Tag */ + goto no_tag; + + if ((tul_msgin_accept(pCurHcb)) == -1) + return (-1); + + if (pCurHcb->HCS_Phase != MSG_IN) + goto no_tag; + + TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN); + if ((wait_tulip(pCurHcb)) == -1) + return (-1); + tag = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* Read Tag ID */ + pCurScb = pCurHcb->HCS_Scb + tag; + if ((pCurScb->SCB_Target != tar) || (pCurScb->SCB_Lun != lun)) + { + return tul_msgout_abort_tag(pCurHcb); + } + if (pCurScb->SCB_Status != SCB_BUSY) + { /* 03/24/95 */ + return tul_msgout_abort_tag(pCurHcb); + } + pCurHcb->HCS_ActScb = pCurScb; + if ((tul_msgin_accept(pCurHcb)) == -1) + return (-1); + } + else /* No tag */ + { +no_tag: + if ((pCurScb = tul_find_busy_scb(pCurHcb, tar | (lun << 8))) == NULL) + { +/*printk("SCB pointer = NULL\n");*/ + return tul_msgout_abort_targ(pCurHcb); + } + pCurHcb->HCS_ActScb = pCurScb; + if (!(pCurTcb->TCS_DrvFlags & TCF_DRV_EN_TAG)) + { + if ((tul_msgin_accept(pCurHcb)) == -1) + return (-1); + } + } + return 0; +} + + +/***************************************************************************/ +int int_tul_bad_seq(HCS *pCurHcb) /* target wrong phase */ +{ + SCB *pCurScb; + int i; + + tul_reset_scsi(pCurHcb, 10); + + while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) + { +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + tul_append_done_scb(pCurHcb, pCurScb); + } + for (i = 0; i < pCurHcb->HCS_MaxTar; i++) + { + pCurHcb->HCS_Tcs[i].TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE);; + } + return (-1); +} + + +/***************************************************************************/ +int tul_msgout_abort_targ(HCS *pCurHcb) +{ + + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + if (tul_msgin_accept(pCurHcb) == -1) + return(-1); + if (pCurHcb->HCS_Phase != MSG_OUT) + return(tul_bad_seq(pCurHcb)); + + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + + return tul_wait_disc(pCurHcb); +} + +/***************************************************************************/ +int tul_msgout_abort_tag(HCS *pCurHcb) +{ + + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + if (tul_msgin_accept(pCurHcb) == -1) + return(-1); + if (pCurHcb->HCS_Phase != MSG_OUT) + return(tul_bad_seq(pCurHcb)); + + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_ABORT_TAG); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + + return tul_wait_disc(pCurHcb); + +} + +/***************************************************************************/ +int tul_msgin(HCS *pCurHcb) +{ + TCS *pCurTcb; + + for (;;) { + + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); + + TUL_WRLONG(pCurHcb->HCS_Base + TUL_SCnt0, 1); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN); + if ((wait_tulip(pCurHcb)) == -1) + return (-1); + + switch (TUL_RD(pCurHcb->HCS_Base, TUL_SFifo)) { + case MSG_DISC: /* Disconnect msg */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT); + + return tul_wait_disc(pCurHcb); + + case MSG_SDP: + case MSG_RESTORE: + case MSG_NOP: + tul_msgin_accept(pCurHcb); + break; + + case MSG_REJ: /* Clear ATN first */ + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, + (TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7))); + pCurTcb = pCurHcb->HCS_ActTcs; + if ((pCurTcb->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) + { /* do sync nego */ + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + } + tul_msgin_accept(pCurHcb); + break; + + case MSG_EXTEND: /* extended msg */ + tul_msgin_extend(pCurHcb); + break; + + case MSG_IGNOREWIDE: + tul_msgin_accept(pCurHcb); + break; + + /* get */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_IN); + if (wait_tulip(pCurHcb) == -1) + return -1; + + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, 0); /* put pad */ + TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get IGNORE field */ + TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); /* get pad */ + + tul_msgin_accept(pCurHcb); + break; + + case MSG_COMP: + { + TUL_WR(pCurHcb->HCS_Base+ TUL_SCtrl0, TSC_FLUSH_FIFO); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_MSG_ACCEPT); + return tul_wait_done_disc(pCurHcb); + } + default: + tul_msgout_reject(pCurHcb); + break; + } + if (pCurHcb->HCS_Phase != MSG_IN) + return (pCurHcb->HCS_Phase); + } + /* statement won't reach here */ +} + + + + +/***************************************************************************/ +int tul_msgout_reject(HCS *pCurHcb) +{ + + TUL_WR(pCurHcb->HCS_Base + TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + + if ((tul_msgin_accept(pCurHcb)) == -1) + return(-1); + + if (pCurHcb->HCS_Phase == MSG_OUT) + { + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_REJ); /* Msg reject */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); + } + return (pCurHcb->HCS_Phase); +} + + + +/***************************************************************************/ +int tul_msgout_ide(HCS *pCurHcb) +{ + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_IDE); /* Initiator Detected Error */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); +} + + +/***************************************************************************/ +int tul_msgin_extend(HCS *pCurHcb) +{ + BYTE len, idx; + + if (tul_msgin_accept(pCurHcb) != MSG_IN) + return (pCurHcb->HCS_Phase); + + /* Get extended msg length */ + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, 1); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_IN); + if (wait_tulip(pCurHcb) == -1) + return (-1); + + len = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + pCurHcb->HCS_Msg[0] = len; + for (idx = 1 ; len != 0; len--) { + + if ((tul_msgin_accept(pCurHcb)) != MSG_IN) + return (pCurHcb->HCS_Phase); + TUL_WRLONG(pCurHcb->HCS_Base+ TUL_SCnt0, 1); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_IN); + if (wait_tulip(pCurHcb) == -1) + return (-1); + pCurHcb->HCS_Msg[idx++] = TUL_RD(pCurHcb->HCS_Base, TUL_SFifo); + } + if (pCurHcb->HCS_Msg[1] == 1) + { /* if it's synchronous data transfer request */ + if (pCurHcb->HCS_Msg[0] != 3) /* if length is not right */ + return (tul_msgout_reject(pCurHcb)); + if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_SYNC_NEGO) + { /* Set OFFSET=0 to do async, nego back */ + pCurHcb->HCS_Msg[3] = 0; + } + else + { + if ((tul_msgin_sync(pCurHcb) == 0) && + (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SYNC_DONE)) + { + tul_sync_done(pCurHcb); + return (tul_msgin_accept(pCurHcb)); + } + } + + TUL_WR(pCurHcb->HCS_Base+ TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + if ((tul_msgin_accept(pCurHcb)) != MSG_OUT) + return (pCurHcb->HCS_Phase); + /* sync msg out */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); +/* TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, TSC_FLUSH_FIFO); */ + + tul_sync_done(pCurHcb); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_EXTEND); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 3); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 1); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurHcb->HCS_Msg[2]); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurHcb->HCS_Msg[3]); + + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); + } + + if ((pCurHcb->HCS_Msg[0] != 2) || (pCurHcb->HCS_Msg[1] != 3)) + return (tul_msgout_reject(pCurHcb)); + /* if it's WIDE DATA XFER REQ */ + if (pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) + { + pCurHcb->HCS_Msg[2] = 0; + } + else + { + if (pCurHcb->HCS_Msg[2] > 2) /* > 32 bits */ + return (tul_msgout_reject(pCurHcb)); + if (pCurHcb->HCS_Msg[2] == 2) /* == 32 */ + { + pCurHcb->HCS_Msg[2] = 1; + } + else + { + if ((pCurHcb->HCS_ActTcs->TCS_Flags & TCF_NO_WDTR) == 0) + { + wdtr_done(pCurHcb); + if ((pCurHcb->HCS_ActTcs->TCS_Flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) + TUL_WR(pCurHcb->HCS_Base+ TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + return (tul_msgin_accept(pCurHcb)); + } + } + } + TUL_WR(pCurHcb->HCS_Base+ TUL_SSignal, ((TUL_RD(pCurHcb->HCS_Base, TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN)); + + if (tul_msgin_accept(pCurHcb) != MSG_OUT) + return (pCurHcb->HCS_Phase); + /* WDTR msg out */ + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, MSG_EXTEND); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 2); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, 3); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurHcb->HCS_Msg[2]); + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_XF_FIFO_OUT); + return (wait_tulip(pCurHcb)); +} + +/***************************************************************************/ +int tul_msgin_sync(HCS *pCurHcb) +{ + char default_period; + + default_period = tul_rate_tbl[pCurHcb->HCS_ActTcs->TCS_Flags & TCF_SCSI_RATE]; + if (pCurHcb->HCS_Msg[3] > MAX_OFFSET) + { + pCurHcb->HCS_Msg[3] = MAX_OFFSET; + if (pCurHcb->HCS_Msg[2] < default_period) + { + pCurHcb->HCS_Msg[2] = default_period; + return 1; + } + if (pCurHcb->HCS_Msg[2] >= 59) + { /* Change to async */ + pCurHcb->HCS_Msg[3] = 0; + } + return 1; + } + + /* offset requests asynchronous transfers ? */ + if (pCurHcb->HCS_Msg[3] == 0) + { + return 0; + } + if (pCurHcb->HCS_Msg[2] < default_period) + { + pCurHcb->HCS_Msg[2] = default_period; + return 1; + } + + if (pCurHcb->HCS_Msg[2] >= 59) + { + pCurHcb->HCS_Msg[3] = 0; + return 1; + } + return 0; +} + + +/***************************************************************************/ +int wdtr_done(HCS *pCurHcb) +{ + pCurHcb->HCS_ActTcs->TCS_Flags &= ~TCF_SYNC_DONE; + pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_WDTR_DONE; + + pCurHcb->HCS_ActTcs->TCS_JS_Period = 0; + if (pCurHcb->HCS_Msg[2]) + { /* if 16 bit */ + pCurHcb->HCS_ActTcs->TCS_JS_Period |= TSC_WIDE_SCSI; + } + pCurHcb->HCS_ActTcs->TCS_SConfig0 &= ~TSC_ALT_PERIOD; + TUL_WR(pCurHcb->HCS_Base+TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0); + TUL_WR(pCurHcb->HCS_Base+TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period); + + return 1; +} + +/***************************************************************************/ +int tul_sync_done(HCS *pCurHcb) +{ + int i; + + pCurHcb->HCS_ActTcs->TCS_Flags |= TCF_SYNC_DONE; + + if (pCurHcb->HCS_Msg[3]) + { + pCurHcb->HCS_ActTcs->TCS_JS_Period |= pCurHcb->HCS_Msg[3]; + for (i = 0; i < 8; i++) + { + if (tul_rate_tbl[i] >= pCurHcb->HCS_Msg[2]) /* pick the big one */ + break; + } + pCurHcb->HCS_ActTcs->TCS_JS_Period |= (i << 4); + pCurHcb->HCS_ActTcs->TCS_SConfig0 |= TSC_ALT_PERIOD; + } + TUL_WR(pCurHcb->HCS_Base+TUL_SConfig, pCurHcb->HCS_ActTcs->TCS_SConfig0); + TUL_WR(pCurHcb->HCS_Base+TUL_SPeriod, pCurHcb->HCS_ActTcs->TCS_JS_Period); + + return (-1); +} + + +int tul_post_scsi_rst(HCS *pCurHcb) +{ + SCB *pCurScb; + TCS *pCurTcb; + int i; + + pCurHcb->HCS_ActScb = 0; + pCurHcb->HCS_ActTcs = 0; + pCurHcb->HCS_Flags = 0; + + while ((pCurScb = tul_pop_busy_scb(pCurHcb)) != NULL) + { +// pCurScb->SCB_Status = SCB_DONE; + pCurScb->SCB_HaStat = HOST_BAD_PHAS; + tul_append_done_scb(pCurHcb, pCurScb); + } + /* clear sync done flag */ + pCurTcb = &pCurHcb->HCS_Tcs[0]; + for (i = 0; i < pCurHcb->HCS_MaxTar; pCurTcb++, i++) + { + pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); + /* Initialize the sync. xfer register values to an asyn xfer */ + pCurTcb->TCS_JS_Period = 0; + pCurTcb->TCS_SConfig0 = pCurHcb->HCS_SConf1; + pCurHcb->HCS_ActTags[0] = 0; /* 07/22/98 */ + pCurHcb->HCS_Tcs[i].TCS_Flags &= ~TCF_BUSY; /* 07/22/98 */ + } /* for */ + +// pCurHcb->HCS_Semaph = 1; /* 07/29/98 */ + return (-1); +} + +/***************************************************************************/ +void tul_select_atn_stop(HCS *pCurHcb, SCB *pCurScb) +{ + pCurScb->SCB_Status |= SCB_SELECT; + pCurScb->SCB_NxtStat = 0x1; + pCurHcb->HCS_ActScb = pCurScb; + pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target]; + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SELATNSTOP); + return; +} + + +/***************************************************************************/ +void tul_select_atn(HCS *pCurHcb, SCB *pCurScb) +{ + int i; + + pCurScb->SCB_Status |= SCB_SELECT; + pCurScb->SCB_NxtStat = 0x2; + + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_Ident); + for (i = 0; i < (int)pCurScb->SCB_CDBLen; i++) + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, pCurScb->SCB_CDB[i]); + pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target]; + pCurHcb->HCS_ActScb = pCurScb; + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_SEL_ATN); + return; +} + +/***************************************************************************/ +void tul_select_atn3(HCS *pCurHcb, SCB *pCurScb) +{ + int i; + + pCurScb->SCB_Status |= SCB_SELECT; + pCurScb->SCB_NxtStat = 0x2; + + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_Ident); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_TagMsg); + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_TagId); + for (i = 0; i < (int) pCurScb->SCB_CDBLen; i++) + TUL_WR(pCurHcb->HCS_Base+ TUL_SFifo, pCurScb->SCB_CDB[i]); + pCurHcb->HCS_ActTcs = &pCurHcb->HCS_Tcs[pCurScb->SCB_Target]; + pCurHcb->HCS_ActScb = pCurScb; + TUL_WR(pCurHcb->HCS_Base+ TUL_SCmd, TSC_SEL_ATN3); + return; +} + +/***************************************************************************/ +/* SCSI Bus Device Reset */ +int tul_bus_device_reset(HCS *pCurHcb) +{ + SCB *pCurScb = pCurHcb->HCS_ActScb; + TCS *pCurTcb = pCurHcb->HCS_ActTcs; + SCB *pTmpScb, *pPrevScb; + BYTE tar; + + if (pCurHcb->HCS_Phase != MSG_OUT) + { + return (int_tul_bad_seq(pCurHcb)); /* Unexpected phase */ + } + + tul_unlink_pend_scb(pCurHcb, pCurScb); +#if 0 + pCurScb->SCB_HaStat = 0; + pCurScb->SCB_Status = SCB_DONE; /* command done */ + tul_append_done_scb(pCurHcb, pCurScb); +#else + tul_release_scb(pCurHcb, pCurScb); +#endif + + + tar = pCurScb->SCB_Target; /* target */ + pCurTcb->TCS_Flags &= ~(TCF_SYNC_DONE|TCF_WDTR_DONE|TCF_BUSY); + /* clr sync. nego & WDTR flags 07/22/98 */ + + /* abort all SCB with same target */ + pPrevScb = pTmpScb = pCurHcb->HCS_FirstBusy; /* Check Busy queue */ + while (pTmpScb != NULL) { + + if (pTmpScb->SCB_Target == tar) + { + /* unlink it */ + if (pTmpScb == pCurHcb->HCS_FirstBusy) + { + if ((pCurHcb->HCS_FirstBusy = pTmpScb->SCB_NxtScb) == NULL) + pCurHcb->HCS_LastBusy = NULL; + } + else + { + pPrevScb->SCB_NxtScb = pTmpScb->SCB_NxtScb; + if (pTmpScb == pCurHcb->HCS_LastBusy) + pCurHcb->HCS_LastBusy = pPrevScb; + } + pTmpScb->SCB_HaStat = HOST_ABORTED; + tul_append_done_scb(pCurHcb, pTmpScb); + } /* Previous haven't change */ + else + { + pPrevScb = pTmpScb; + } + pTmpScb = pTmpScb->SCB_NxtScb; + } + + TUL_WR(pCurHcb->HCS_Base + TUL_SFifo, MSG_DEVRST); + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_XF_FIFO_OUT); + + return tul_wait_disc(pCurHcb); + +} + +/***************************************************************************/ +int tul_msgin_accept(HCS *pCurHcb) +{ + TUL_WR(pCurHcb->HCS_Base + TUL_SCmd, TSC_MSG_ACCEPT); + return (wait_tulip(pCurHcb)); +} + +/***************************************************************************/ +int wait_tulip(HCS *pCurHcb) +{ +/* register BYTE s; + int phase;*/ +// ULONG flags; + +// pCurHcb->HCS_Flags &= ~HCF_RESET; + + while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base,TUL_SStatus0)) + & TSS_INT_PENDING)) { +// save_flags(flags); /* wh 07/21/98 */ +// sti(); +// restore_flags(flags); /* Addedby hc 07/21/98 */ + +// if (pCurHcb->HCS_Flags & HCF_RESET) { +// return -1; +// } + } + + pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt); + pCurHcb->HCS_Phase = pCurHcb->HCS_JSStatus0 & TSS_PH_MASK; + pCurHcb->HCS_JSStatus1 = TUL_RD(pCurHcb->HCS_Base, TUL_SStatus1); + +/* stat2 = TUL_RD(pCurHcb->HCS_Base,TUL_SStatus2); + printk("SCSI int status = %x", pCurHcb->HCS_JSInt);*/ + + if (pCurHcb->HCS_JSInt & TSS_RESEL_INT) + { /* if SCSI bus reset detected */ + return (int_tul_resel(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & TSS_SEL_TIMEOUT) + { /* if selected/reselected timeout interrupt */ + return (int_tul_busfree(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) + { /* if SCSI bus reset detected */ + return (int_tul_scsi_rst(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & TSS_DISC_INT) + { /* BUS disconnection */ + if (pCurHcb->HCS_Flags & HCF_EXPECT_DONE_DISC) + { + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */ + tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb); +// pCurHcb->HCS_ActScb->SCB_Status = SCB_DONE; + pCurHcb->HCS_ActScb->SCB_HaStat = 0; + tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb); + pCurHcb->HCS_ActScb = NULL; + pCurHcb->HCS_ActTcs = NULL; + pCurHcb->HCS_Flags &= ~HCF_EXPECT_DONE_DISC; + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);/* Enable HW reselect */ + return (-1); + } + if (pCurHcb->HCS_Flags & HCF_EXPECT_DISC) + { + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */ + pCurHcb->HCS_ActScb = NULL; + pCurHcb->HCS_ActTcs = NULL; + pCurHcb->HCS_Flags &= ~HCF_EXPECT_DISC; + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);/* Enable HW reselect */ + return (-1); + } + return (int_tul_busfree(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & (TSS_FUNC_COMP | TSS_BUS_SERV)) + { + return (pCurHcb->HCS_Phase); + } + + return (pCurHcb->HCS_Phase); +} +/***************************************************************************/ +int tul_wait_disc(HCS *pCurHcb) +{ + + while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base,TUL_SStatus0)) + & TSS_INT_PENDING)) + ; + + + pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt); + + if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) + { /* if SCSI bus reset detected */ + return (int_tul_scsi_rst(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & TSS_DISC_INT) + { /* BUS disconnection */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */ + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);/* Enable HW reselect */ + pCurHcb->HCS_ActScb = NULL; + return (-1); + } + + return (tul_bad_seq(pCurHcb)); +} + +/***************************************************************************/ +int tul_wait_done_disc(HCS *pCurHcb) +{ + + + while (!((pCurHcb->HCS_JSStatus0 = TUL_RD(pCurHcb->HCS_Base,TUL_SStatus0)) + & TSS_INT_PENDING)) + ; + + pCurHcb->HCS_JSInt = TUL_RD(pCurHcb->HCS_Base, TUL_SInt); + + + if (pCurHcb->HCS_JSInt & TSS_SCSIRST_INT) + { /* if SCSI bus reset detected */ + return (int_tul_scsi_rst(pCurHcb)); + } + + if (pCurHcb->HCS_JSInt & TSS_DISC_INT) + { /* BUS disconnection */ + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl0, TSC_FLUSH_FIFO); /* Flush SCSI FIFO */ + TUL_WR(pCurHcb->HCS_Base + TUL_SConfig, TSC_INITDEFAULT); + TUL_WR(pCurHcb->HCS_Base + TUL_SCtrl1, TSC_HW_RESELECT);/* Enable HW reselect */ + tul_unlink_busy_scb(pCurHcb, pCurHcb->HCS_ActScb); + + tul_append_done_scb(pCurHcb, pCurHcb->HCS_ActScb); + pCurHcb->HCS_ActScb = NULL; + return (-1); + } + + + return (tul_bad_seq(pCurHcb)); +} + +/**************************** EOF *********************************/ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/i91uscsi.h linux/drivers/scsi/i91uscsi.h --- linux.vanilla/drivers/scsi/i91uscsi.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/i91uscsi.h Wed Dec 9 09:52:25 1998 @@ -0,0 +1,890 @@ + +/************************************************************************** + * Initio 9100 device driver for Linux. + * + * Copyright (c) 1994-1998 Initio Corporation + * All rights reserved. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + **************************************************************************/ + +#define ULONG unsigned long +#define USHORT unsigned short +#define UCHAR unsigned char +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned long +#define UBYTE unsigned char +#define UWORD unsigned short +#define UDWORD unsigned long +#ifdef ALPHA +#define U32 unsigned int +#else +#define U32 unsigned long +#endif + +#ifndef NULL +#define NULL 0 /* zero */ +#endif +#ifndef TRUE +#define TRUE (1) /* boolean true */ +#endif +#ifndef FALSE +#define FALSE (0) /* boolean false */ +#endif +#ifndef FAILURE +#define FAILURE (-1) +#endif +/*#endif*/ + +#define TOTAL_SG_ENTRY 32 +#define MAX_SUPPORTED_ADAPTERS 8 +#define MAX_OFFSET 15 +#define MAX_TARGETS 16 + +#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */ +#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */ +#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */ +#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */ + +//#if defined(__KERNEL__) + +#define _I91USCSI_H +#if 0 +#include "ini9100u.h" +#endif + +typedef struct { + unsigned short base; + unsigned short vec; +} i91u_config ; + +/***************************************/ +/* Tulip Configuration Register Set */ +/***************************************/ +#define TUL_PVID 0x00 /* Vendor ID */ +#define TUL_PDID 0x02 /* Device ID */ +#define TUL_PCMD 0x04 /* Command */ +#define TUL_PSTUS 0x06 /* Status */ +#define TUL_PRID 0x08 /* Revision number */ +#define TUL_PPI 0x09 /* Programming interface */ +#define TUL_PSC 0x0A /* Sub Class */ +#define TUL_PBC 0x0B /* Base Class */ +#define TUL_PCLS 0x0C /* Cache line size */ +#define TUL_PLTR 0x0D /* Latency timer */ +#define TUL_PHDT 0x0E /* Header type */ +#define TUL_PBIST 0x0F /* BIST */ +#define TUL_PBAD 0x10 /* Base address */ +#define TUL_PBAD1 0x14 /* Base address */ +#define TUL_PBAD2 0x18 /* Base address */ +#define TUL_PBAD3 0x1C /* Base address */ +#define TUL_PBAD4 0x20 /* Base address */ +#define TUL_PBAD5 0x24 /* Base address */ +#define TUL_PRSVD 0x28 /* Reserved */ +#define TUL_PRSVD1 0x2C /* Reserved */ +#define TUL_PRAD 0x30 /* Expansion ROM base address */ +#define TUL_PRSVD2 0x34 /* Reserved */ +#define TUL_PRSVD3 0x38 /* Reserved */ +#define TUL_PINTL 0x3C /* Interrupt line */ +#define TUL_PINTP 0x3D /* Interrupt pin */ +#define TUL_PIGNT 0x3E /* MIN_GNT */ +#define TUL_PMGNT 0x3F /* MAX_GNT */ + +/************************/ +/* Jasmin Register Set */ +/************************/ +#define TUL_HACFG0 0x40 /* H/A Configuration Register 0 */ +#define TUL_HACFG1 0x41 /* H/A Configuration Register 1 */ +#define TUL_HACFG2 0x42 /* H/A Configuration Register 2 */ + +#define TUL_SDCFG0 0x44 /* SCSI Device Configuration 0 */ +#define TUL_SDCFG1 0x45 /* SCSI Device Configuration 1 */ +#define TUL_SDCFG2 0x46 /* SCSI Device Configuration 2 */ +#define TUL_SDCFG3 0x47 /* SCSI Device Configuration 3 */ + +#define TUL_GINTS 0x50 /* Global Interrupt Status Register */ +#define TUL_GIMSK 0x52 /* Global Interrupt MASK Register */ +#define TUL_GCTRL 0x54 /* Global Control Register */ + #define TUL_GCTRL_EEPROM_BIT 0x04 +#define TUL_GCTRL1 0x55 /* Global Control Register */ +#define TUL_DMACFG 0x5B /* DMA configuration */ +#define TUL_NVRAM 0x5D /* Non-volatile RAM port */ + +#define TUL_SCnt0 0x80 /* 00 R/W Transfer Counter Low */ +#define TUL_SCnt1 0x81 /* 01 R/W Transfer Counter Mid */ +#define TUL_SCnt2 0x82 /* 02 R/W Transfer Count High */ +#define TUL_SFifoCnt 0x83 /* 03 R FIFO counter */ +#define TUL_SIntEnable 0x84 /* 03 W Interrupt enble */ +#define TUL_SInt 0x84 /* 04 R Interrupt Register */ +#define TUL_SCtrl0 0x85 /* 05 W Control 0 */ +#define TUL_SStatus0 0x85 /* 05 R Status 0 */ +#define TUL_SCtrl1 0x86 /* 06 W Control 1 */ +#define TUL_SStatus1 0x86 /* 06 R Status 1 */ +#define TUL_SConfig 0x87 /* 07 W Configuration */ +#define TUL_SStatus2 0x87 /* 07 R Status 2 */ +#define TUL_SPeriod 0x88 /* 08 W Sync. Transfer Period & Offset*/ +#define TUL_SOffset 0x88 /* 08 R Offset */ +#define TUL_SScsiId 0x89 /* 09 W SCSI ID */ +#define TUL_SBusId 0x89 /* 09 R SCSI BUS ID */ +#define TUL_STimeOut 0x8A /* 0A W Sel/Resel Time Out Register */ +#define TUL_SIdent 0x8A /* 0A R Identify Message Register */ +#define TUL_SAvail 0x8A /* 0A R Availiable Counter Register */ +#define TUL_SData 0x8B /* 0B R/W SCSI data in/out */ +#define TUL_SFifo 0x8C /* 0C R/W FIFO */ +#define TUL_SSignal 0x90 /* 10 R/W SCSI signal in/out */ +#define TUL_SCmd 0x91 /* 11 R/W Command */ +#define TUL_STest0 0x92 /* 12 R/W Test0 */ +#define TUL_STest1 0x93 /* 13 R/W Test1 */ +#define TUL_SCFG1 0x94 /* 14 R/W Configuration */ + +#define TUL_XAddH 0xC0 /*DMA Transfer Physical Address */ +#define TUL_XAddW 0xC8 /*DMA Current Transfer Physical Address */ +#define TUL_XCntH 0xD0 /*DMA Transfer Counter */ +#define TUL_XCntW 0xD4 /*DMA Current Transfer Counter */ +#define TUL_XCmd 0xD8 /*DMA Command Register */ +#define TUL_Int 0xDC /*Interrupt Register */ +#define TUL_XStatus 0xDD /*DMA status Register */ +#define TUL_Mask 0xE0 /*Interrupt Mask Register */ +#define TUL_XCtrl 0xE4 /*DMA Control Register */ +#define TUL_XCtrl1 0xE5 /*DMA Control Register 1 */ +#define TUL_XFifo 0xE8 /*DMA FIFO */ + +#define TUL_WCtrl 0xF7 /*Bus master wait state control */ +#define TUL_DCtrl 0xFB /*DMA delay control */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Command register of Configuration Space Header */ +/*----------------------------------------------------------------------*/ +#define BUSMS 0x04 /* BUS MASTER Enable */ +#define IOSPA 0x01 /* IO Space Enable */ + +/*----------------------------------------------------------------------*/ +/* Command Codes of Tulip SCSI Command register */ +/*----------------------------------------------------------------------*/ +#define TSC_EN_RESEL 0x80 /* Enable Reselection */ +#define TSC_CMD_COMP 0x84 /* Command Complete Sequence */ +#define TSC_SEL 0x01 /* Select Without ATN Sequence */ +#define TSC_SEL_ATN 0x11 /* Select With ATN Sequence */ +#define TSC_SEL_ATN_DMA 0x51 /* Select With ATN Sequence with DMA */ +#define TSC_SEL_ATN3 0x31 /* Select With ATN3 Sequence */ +#define TSC_SEL_ATNSTOP 0x12 /* Select With ATN and Stop Sequence */ +#define TSC_SELATNSTOP 0x1E /* Select With ATN and Stop Sequence */ + +#define TSC_SEL_ATN_DIRECT_IN 0x95 /* Select With ATN Sequence */ +#define TSC_SEL_ATN_DIRECT_OUT 0x15 /* Select With ATN Sequence */ +#define TSC_SEL_ATN3_DIRECT_IN 0xB5 /* Select With ATN3 Sequence */ +#define TSC_SEL_ATN3_DIRECT_OUT 0x35 /* Select With ATN3 Sequence */ +#define TSC_XF_DMA_OUT_DIRECT 0x06 /* DMA Xfer Infomation out */ +#define TSC_XF_DMA_IN_DIRECT 0x86 /* DMA Xfer Infomation in */ + +#define TSC_XF_DMA_OUT 0x43 /* DMA Xfer Infomation out */ +#define TSC_XF_DMA_IN 0xC3 /* DMA Xfer Infomation in */ +#define TSC_XF_FIFO_OUT 0x03 /* FIFO Xfer Infomation out */ +#define TSC_XF_FIFO_IN 0x83 /* FIFO Xfer Infomation in */ + +#define TSC_MSG_ACCEPT 0x0F /* Message Accept */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Control 0 Register */ +/*----------------------------------------------------------------------*/ +#define TSC_RST_SEQ 0x20 /* Reset sequence counter */ +#define TSC_FLUSH_FIFO 0x10 /* Flush FIFO */ +#define TSC_ABT_CMD 0x04 /* Abort command (sequence) */ +#define TSC_RST_CHIP 0x02 /* Reset SCSI Chip */ +#define TSC_RST_BUS 0x01 /* Reset SCSI Bus */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Control 1 Register */ +/*----------------------------------------------------------------------*/ +#define TSC_EN_SCAM 0x80 /* Enable SCAM */ +#define TSC_TIMER 0x40 /* Select timeout unit */ +#define TSC_EN_SCSI2 0x20 /* SCSI-2 mode */ +#define TSC_PWDN 0x10 /* Power down mode */ +#define TSC_WIDE_CPU 0x08 /* Wide CPU */ +#define TSC_HW_RESELECT 0x04 /* Enable HW reselect */ +#define TSC_EN_BUS_OUT 0x02 /* Enable SCSI data bus out latch */ +#define TSC_EN_BUS_IN 0x01 /* Enable SCSI data bus in latch */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Configuration Register */ +/*----------------------------------------------------------------------*/ +#define TSC_EN_LATCH 0x80 /* Enable phase latch */ +#define TSC_INITIATOR 0x40 /* Initiator mode */ +#define TSC_EN_SCSI_PAR 0x20 /* Enable SCSI parity */ +#define TSC_DMA_8BIT 0x10 /* Alternate dma 8-bits mode */ +#define TSC_DMA_16BIT 0x08 /* Alternate dma 16-bits mode */ +#define TSC_EN_WDACK 0x04 /* Enable DACK while wide SCSI xfer */ +#define TSC_ALT_PERIOD 0x02 /* Alternate sync period mode */ +#define TSC_DIS_SCSIRST 0x01 /* Disable SCSI bus reset us */ + +#define TSC_INITDEFAULT (TSC_INITIATOR | TSC_EN_LATCH | TSC_ALT_PERIOD | TSC_DIS_SCSIRST) + +#define TSC_WIDE_SCSI 0x80 /* Enable Wide SCSI */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI signal Register */ +/*----------------------------------------------------------------------*/ +#define TSC_RST_ACK 0x00 /* Release ACK signal */ +#define TSC_RST_ATN 0x00 /* Release ATN signal */ +#define TSC_RST_BSY 0x00 /* Release BSY signal */ + +#define TSC_SET_ACK 0x40 /* ACK signal */ +#define TSC_SET_ATN 0x08 /* ATN signal */ + +#define TSC_REQI 0x80 /* REQ signal */ +#define TSC_ACKI 0x40 /* ACK signal */ +#define TSC_BSYI 0x20 /* BSY signal */ +#define TSC_SELI 0x10 /* SEL signal */ +#define TSC_ATNI 0x08 /* ATN signal */ +#define TSC_MSGI 0x04 /* MSG signal */ +#define TSC_CDI 0x02 /* C/D signal */ +#define TSC_IOI 0x01 /* I/O signal */ + + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Status 0 Register */ +/*----------------------------------------------------------------------*/ +#define TSS_INT_PENDING 0x80 /* Interrupt pending */ +#define TSS_SEQ_ACTIVE 0x40 /* Sequencer active */ +#define TSS_XFER_CNT 0x20 /* Transfer counter zero */ +#define TSS_FIFO_EMPTY 0x10 /* FIFO empty */ +#define TSS_PAR_ERROR 0x08 /* SCSI parity error */ +#define TSS_PH_MASK 0x07 /* SCSI phase mask */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Status 1 Register */ +/*----------------------------------------------------------------------*/ +#define TSS_STATUS_RCV 0x08 /* Status received */ +#define TSS_MSG_SEND 0x40 /* Message sent */ +#define TSS_CMD_PH_CMP 0x20 /* command phase done */ +#define TSS_DATA_PH_CMP 0x10 /* Data phase done */ +#define TSS_STATUS_SEND 0x08 /* Status sent */ +#define TSS_XFER_CMP 0x04 /* Transfer completed */ +#define TSS_SEL_CMP 0x02 /* Selection completed */ +#define TSS_ARB_CMP 0x01 /* Arbitration completed */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Status 2 Register */ +/*----------------------------------------------------------------------*/ +#define TSS_CMD_ABTED 0x80 /* Command aborted */ +#define TSS_OFFSET_0 0x40 /* Offset counter zero */ +#define TSS_FIFO_FULL 0x20 /* FIFO full */ +#define TSS_TIMEOUT_0 0x10 /* Timeout counter zero */ +#define TSS_BUSY_RLS 0x08 /* Busy release */ +#define TSS_PH_MISMATCH 0x04 /* Phase mismatch */ +#define TSS_SCSI_BUS_EN 0x02 /* SCSI data bus enable */ +#define TSS_SCSIRST 0x01 /* SCSI bus reset in progress */ + +/*----------------------------------------------------------------------*/ +/* bit definition for Tulip SCSI Interrupt Register */ +/*----------------------------------------------------------------------*/ +#define TSS_RESEL_INT 0x80 /* Reselected interrupt */ +#define TSS_SEL_TIMEOUT 0x40 /* Selected/reselected timeout */ +#define TSS_BUS_SERV 0x20 +#define TSS_SCSIRST_INT 0x10 /* SCSI bus reset detected */ +#define TSS_DISC_INT 0x08 /* Disconnected interrupt */ +#define TSS_SEL_INT 0x04 /* Select interrupt */ +#define TSS_SCAM_SEL 0x02 /* SCAM selected */ +#define TSS_FUNC_COMP 0x01 + +/*----------------------------------------------------------------------*/ +/* SCSI Phase Codes. */ +/*----------------------------------------------------------------------*/ +#define DATA_OUT 0 +#define DATA_IN 1 /* 4 */ +#define CMD_OUT 2 +#define STATUS_IN 3 /* 6 */ +#define MSG_OUT 6 /* 3 */ +#define MSG_IN 7 + + + +/*----------------------------------------------------------------------*/ +/* Command Codes of Tulip xfer Command register */ +/*----------------------------------------------------------------------*/ +#define TAX_X_FORC 0x02 +#define TAX_X_ABT 0x04 +#define TAX_X_CLR_FIFO 0x08 + +#define TAX_X_IN 0x21 +#define TAX_X_OUT 0x01 +#define TAX_SG_IN 0xA1 +#define TAX_SG_OUT 0x81 + +/*----------------------------------------------------------------------*/ +/* Tulip Interrupt Register */ +/*----------------------------------------------------------------------*/ +#define XCMP 0x01 +#define FCMP 0x02 +#define XABT 0x04 +#define XERR 0x08 +#define SCMP 0x10 +#define IPEND 0x80 + +/*----------------------------------------------------------------------*/ +/* Tulip DMA Status Register */ +/*----------------------------------------------------------------------*/ +#define XPEND 0x01 /* Transfer pending */ +#define FEMPTY 0x02 /* FIFO empty */ + + + +/*----------------------------------------------------------------------*/ +/* bit definition for TUL_GCTRL */ +/*----------------------------------------------------------------------*/ +#define EXTSG 0x80 +#define EXTAD 0x60 +#define SEG4K 0x08 +#define EEPRG 0x04 +#define MRMUL 0x02 + +/*----------------------------------------------------------------------*/ +/* bit definition for TUL_NVRAM */ +/*----------------------------------------------------------------------*/ +#define SE2CS 0x08 +#define SE2CLK 0x04 +#define SE2DO 0x02 +#define SE2DI 0x01 + + +/************************************************************************/ +/* Scatter-Gather Element Structure */ +/************************************************************************/ +typedef struct SG_Struc { + U32 SG_Ptr; /* Data Pointer */ + U32 SG_Len; /* Data Length */ +} SG; + +/*********************************************************************** + SCSI Control Block +************************************************************************/ +typedef struct Scsi_Ctrl_Blk { + struct Scsi_Ctrl_Blk *SCB_NxtScb; + UBYTE SCB_Status; /*4 */ + UBYTE SCB_NxtStat; /*5 */ + UBYTE SCB_Mode; /*6 */ + UBYTE SCB_Msgin; /*7 SCB_Res0 */ + UWORD SCB_SGIdx; /*8 */ + UWORD SCB_SGMax; /*A */ +#ifdef ALPHA + U32 SCB_Reserved[2];/*C */ +#else + U32 SCB_Reserved[3];/*C */ +#endif + + U32 SCB_XferLen; /*18 Current xfer len */ + U32 SCB_TotXLen; /*1C Total xfer len */ + U32 SCB_PAddr; /*20 SCB phy. Addr. */ + + UBYTE SCB_Opcode; /*24 SCB command code */ + UBYTE SCB_Flags; /*25 SCB Flags */ + UBYTE SCB_Target; /*26 Target Id */ + UBYTE SCB_Lun; /*27 Lun */ + U32 SCB_BufPtr; /*28 Data Buffer Pointer */ + U32 SCB_BufLen; /*2C Data Allocation Length */ + UBYTE SCB_SGLen; /*30 SG list # */ + UBYTE SCB_SenseLen; /*31 Sense Allocation Length */ + UBYTE SCB_HaStat; /*32 */ + UBYTE SCB_TaStat; /*33 */ + UBYTE SCB_CDBLen; /*34 CDB Length */ + UBYTE SCB_Ident; /*35 Identify */ + UBYTE SCB_TagMsg; /*36 Tag Message */ + UBYTE SCB_TagId; /*37 Queue Tag */ + UBYTE SCB_CDB[12]; /*38 */ + U32 SCB_SGPAddr; /*44 SG List/Sense Buf phy. Addr. */ + U32 SCB_SensePtr; /*48 Sense data pointer */ + void (* SCB_Post)(BYTE *, BYTE *); /*4C POST routine */ + unsigned char *SCB_Srb; /*50 SRB Pointer */ +// Scsi_Cmnd *SCB_Srb; /*2C SRB Pointer */ + SG SCB_SGList[TOTAL_SG_ENTRY]; /*54 Start of SG list */ +} SCB; + +/* Bit Definition for SCB_Status */ +#define SCB_RENT 0x01 +#define SCB_PEND 0x02 +#define SCB_CONTIG 0x04 /* Contigent Allegiance */ +#define SCB_SELECT 0x08 +#define SCB_BUSY 0x10 +#define SCB_DONE 0x20 + + +/* Opcodes of SCB_Opcode */ +#define ExecSCSI 0x1 +#define BusDevRst 0x2 +#define AbortCmd 0x3 + + +/* Bit Definition for SCB_Mode */ +#define SCM_RSENS 0x01 /* request sense mode */ + + +/* Bit Definition for SCB_Flags */ +#define SCF_DONE 0x01 +#define SCF_POST 0x02 +#define SCF_SENSE 0x04 +#define SCF_DIR 0x18 +#define SCF_NO_DCHK 0x00 +#define SCF_DIN 0x08 +#define SCF_DOUT 0x10 +#define SCF_NO_XF 0x18 +#define SCF_WR_VF 0x20 /* Write verify turn on */ +#define SCF_POLL 0x40 +#define SCF_SG 0x80 + +/* Error Codes for SCB_HaStat */ +#define HOST_SEL_TOUT 0x11 +#define HOST_DO_DU 0x12 +#define HOST_BUS_FREE 0x13 +#define HOST_BAD_PHAS 0x14 +#define HOST_INV_CMD 0x16 +#define HOST_ABORTED 0x1A /* 07/21/98 */ +#define HOST_SCSI_RST 0x1B +#define HOST_DEV_RST 0x1C + +/* Error Codes for SCB_TaStat */ +#define TARGET_CHKCOND 0x02 +#define TARGET_BUSY 0x08 +#define QUEUE_FULL 0x28 + +/* SCSI MESSAGE */ +#define MSG_COMP 0x00 +#define MSG_EXTEND 0x01 +#define MSG_SDP 0x02 +#define MSG_RESTORE 0x03 +#define MSG_DISC 0x04 +#define MSG_IDE 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJ 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY 0x09 +#define MSG_LINK_COMP 0x0A +#define MSG_LINK_FLAG 0x0B +#define MSG_DEVRST 0x0C +#define MSG_ABORT_TAG 0x0D + +/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */ +#define MSG_STAG 0x20 +#define MSG_HTAG 0x21 +#define MSG_OTAG 0x22 + +#define MSG_IGNOREWIDE 0x23 + +#define MSG_IDENT 0x80 + +/*********************************************************************** + Target Device Control Structure +**********************************************************************/ + +typedef struct Tar_Ctrl_Struc { + UWORD TCS_Flags; /* 0 */ + UBYTE TCS_JS_Period; /* 2 */ + UBYTE TCS_SConfig0; /* 3 */ + + UWORD TCS_DrvFlags; /* 4 */ + UBYTE TCS_DrvHead; /* 6 */ + UBYTE TCS_DrvSector; /* 7 */ +} TCS; + +/*********************************************************************** + Target Device Control Structure +**********************************************************************/ + +/* Bit Definition for TCF_Flags */ +#define TCF_SCSI_RATE 0x0007 +#define TCF_EN_DISC 0x0008 +#define TCF_NO_SYNC_NEGO 0x0010 +#define TCF_NO_WDTR 0x0020 +#define TCF_EN_255 0x0040 +#define TCF_EN_START 0x0080 +#define TCF_WDTR_DONE 0x0100 +#define TCF_SYNC_DONE 0x0200 +#define TCF_BUSY 0x0400 + + +/* Bit Definition for TCF_DrvFlags */ +#define TCF_DRV_BUSY 0x01 /* Indicate target busy(driver) */ +#define TCF_DRV_EN_TAG 0x0800 +#define TCF_DRV_255_63 0x0400 + +typedef struct I91u_Adpt_Struc { + UWORD ADPT_BIOS; /* 0 */ + UWORD ADPT_BASE; /* 1 */ + UBYTE ADPT_Bus; /* 2 */ + UBYTE ADPT_Device; /* 3 */ + UBYTE ADPT_INTR; /* 4 */ +} INI_ADPT_STRUCT; + + +/*********************************************************************** + Host Adapter Control Structure +************************************************************************/ +typedef struct Ha_Ctrl_Struc { + UWORD HCS_Base; /* 00 */ + UWORD HCS_BIOS; /* 02 */ + UBYTE HCS_Intr; /* 04 */ + UBYTE HCS_SCSI_ID; /* 05 */ + UBYTE HCS_MaxTar; /* 06 */ + UBYTE HCS_NumScbs; /* 07 */ + + UBYTE HCS_Flags; /* 08 */ + UBYTE HCS_Index; /* 09 */ + UBYTE HCS_HaId; /* 0A */ + UBYTE HCS_Config; /* 0B */ + UWORD HCS_IdMask; /* 0C */ + UBYTE HCS_Semaph; /* 0E */ + UBYTE HCS_Phase; /* 0F */ + UBYTE HCS_JSStatus0; /* 10 */ + UBYTE HCS_JSInt; /* 11 */ + UBYTE HCS_JSStatus1; /* 12 */ + UBYTE HCS_SConf1; /* 13 */ + + UBYTE HCS_Msg[8]; /* 14 */ + SCB *HCS_NxtAvail; /* 1C */ + SCB *HCS_Scb; /* 20 */ + SCB *HCS_ScbEnd; /* 24 */ + SCB *HCS_NxtPend; /* 28 */ + SCB *HCS_NxtContig; /* 2C */ + SCB *HCS_ActScb; /* 30 */ + TCS *HCS_ActTcs; /* 34 */ + + SCB *HCS_FirstAvail;/* 38 */ + SCB *HCS_LastAvail; /* 3C */ + SCB *HCS_FirstPend; /* 40 */ + SCB *HCS_LastPend; /* 44 */ + SCB *HCS_FirstBusy; /* 48 */ + SCB *HCS_LastBusy; /* 4C */ + SCB *HCS_FirstDone; /* 50 */ + SCB *HCS_LastDone; /* 54 */ +#if 1 /* HILAM */ + UBYTE HCS_MaxTags[16];/* 58 */ + UBYTE HCS_ActTags[16];/* 68 */ + TCS HCS_Tcs[MAX_TARGETS]; /* 78 */ + ULONG pSRB_head; /* SRB save queue header */ + ULONG pSRB_tail; /* SRB save queue tail */ +#else + ULONG pSRB_head; /* 58 SRB save queue header */ + ULONG pSRB_tail; /* 5C SRB save queue tail */ + UBYTE HCS_MaxTags[16];/* 60 */ + UBYTE HCS_ActTags[16];/* 70 */ + TCS HCS_Tcs[MAX_TARGETS]; /* 78 */ +#endif +// Scsi_Cmnd *pSRB_head; /* B4 SRB save queue header */ +// Scsi_Cmnd *pSRB_tail; /* B8 SRB save queue tail */ +} HCS; + +/* Bit Definition for HCB_Config */ +#define HCC_SCSI_RESET 0x01 +#define HCC_EN_PAR 0x02 +#define HCC_ACT_TERM1 0x04 +#define HCC_ACT_TERM2 0x08 +#define HCC_AUTO_TERM 0x10 +#define HCC_EN_PWR 0x80 + +/* Bit Definition for HCB_Flags */ +#define HCF_EXPECT_DISC 0x01 +#define HCF_EXPECT_SELECT 0x02 +#define HCF_EXPECT_RESET 0x10 +#define HCF_EXPECT_DONE_DISC 0x20 + +/****************************************************************** + Serial EEProm +*******************************************************************/ + +typedef struct _NVRAM_SCSI { /* SCSI channel configuration */ + UCHAR NVM_ChSCSIID; /* 0Ch -> Channel SCSI ID */ + UCHAR NVM_ChConfig1; /* 0Dh -> Channel config 1 */ + UCHAR NVM_ChConfig2; /* 0Eh -> Channel config 2 */ + UCHAR NVM_NumOfTarg; /* 0Fh -> Number of SCSI target */ + /* SCSI target configuration */ + UCHAR NVM_Targ0Config; /* 10h -> Target 0 configuration*/ + UCHAR NVM_Targ1Config; /* 11h -> Target 1 configuration*/ + UCHAR NVM_Targ2Config; /* 12h -> Target 2 configuration*/ + UCHAR NVM_Targ3Config; /* 13h -> Target 3 configuration*/ + UCHAR NVM_Targ4Config; /* 14h -> Target 4 configuration*/ + UCHAR NVM_Targ5Config; /* 15h -> Target 5 configuration*/ + UCHAR NVM_Targ6Config; /* 16h -> Target 6 configuration*/ + UCHAR NVM_Targ7Config; /* 17h -> Target 7 configuration*/ + UCHAR NVM_Targ8Config; /* 18h -> Target 8 configuration*/ + UCHAR NVM_Targ9Config; /* 19h -> Target 9 configuration*/ + UCHAR NVM_TargAConfig; /* 1Ah -> Target A configuration*/ + UCHAR NVM_TargBConfig; /* 1Bh -> Target B configuration*/ + UCHAR NVM_TargCConfig; /* 1Ch -> Target C configuration*/ + UCHAR NVM_TargDConfig; /* 1Dh -> Target D configuration*/ + UCHAR NVM_TargEConfig; /* 1Eh -> Target E configuration*/ + UCHAR NVM_TargFConfig; /* 1Fh -> Target F configuration*/ +} NVRAM_SCSI; + +typedef struct _NVRAM { + /*----------header ---------------*/ + USHORT NVM_Signature; /* 0,1: Signature */ + UCHAR NVM_Size; /* 2: Size of data structure */ + UCHAR NVM_Revision; /* 3: Revision of data structure */ + /* ----Host Adapter Structure ----*/ + UCHAR NVM_ModelByte0; /* 4: Model number (byte 0) */ + UCHAR NVM_ModelByte1; /* 5: Model number (byte 1) */ + UCHAR NVM_ModelInfo; /* 6: Model information */ + UCHAR NVM_NumOfCh; /* 7: Number of SCSI channel*/ + UCHAR NVM_BIOSConfig1; /* 8: BIOS configuration 1 */ + UCHAR NVM_BIOSConfig2; /* 9: BIOS configuration 2 */ + UCHAR NVM_HAConfig1; /* A: Hoat adapter configuration 1 */ + UCHAR NVM_HAConfig2; /* B: Hoat adapter configuration 2 */ + NVRAM_SCSI NVM_SCSIInfo[2]; + UCHAR NVM_reserved[10]; + /* ---------- CheckSum ---------- */ + USHORT NVM_CheckSum; /* 0x3E, 0x3F: Checksum of NVRam */ + } NVRAM, *PNVRAM; + +/* Bios Configuration for nvram->BIOSConfig1 */ +#define NBC1_ENABLE 0x01 /* BIOS enable */ +#define NBC1_8DRIVE 0x02 /* Support more than 2 drives */ +#define NBC1_REMOVABLE 0x04 /* Support removable drive */ +#define NBC1_INT19 0x08 /* Intercept int 19h */ +#define NBC1_BIOSSCAN 0x10 /* Dynamic BIOS scan */ +#define NBC1_LUNSUPPORT 0x40 /* Support LUN */ + +/* HA Configuration Byte 1 */ +#define NHC1_BOOTIDMASK 0x0F /* Boot ID number */ +#define NHC1_LUNMASK 0x70 /* Boot LUN number */ +#define NHC1_CHANMASK 0x80 /* Boot Channel number */ + +/* Bit definition for nvram->SCSIconfig1 */ +#define NCC1_BUSRESET 0x01 /* Reset SCSI bus at power up */ +#define NCC1_PARITYCHK 0x02 /* SCSI parity enable */ +#define NCC1_ACTTERM1 0x04 /* Enable active terminator 1 */ +#define NCC1_ACTTERM2 0x08 /* Enable active terminator 2 */ +#define NCC1_AUTOTERM 0x10 /* Enable auto terminator */ +#define NCC1_PWRMGR 0x80 /* Enable power management */ + +/* Bit definition for SCSI Target configuration byte */ +#define NTC_DISCONNECT 0x08 /* Enable SCSI disconnect */ +#define NTC_SYNC 0x10 /* SYNC_NEGO */ +#define NTC_NO_WDTR 0x20 /* SYNC_NEGO */ +#define NTC_1GIGA 0x40 /* 255 head / 63 sectors (64/32)*/ +#define NTC_SPINUP 0x80 /* Start disk drive */ + +/* Default NVRam values */ +#define INI_SIGNATURE 0xC925 +#define NBC1_DEFAULT (NBC1_ENABLE) +#define NCC1_DEFAULT (NCC1_BUSRESET | NCC1_AUTOTERM | NCC1_PARITYCHK) +#define NTC_DEFAULT (NTC_NO_WDTR | NTC_1GIGA | NTC_DISCONNECT) + +/* SCSI related definition */ +#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */ +#define DISC_ALLOW 0xC0 /* Disconnect is allowed */ +#define SCSICMD_RequestSense 0x03 + + +/*----------------------------------------------------------------------*/ +/* PCI */ +/*----------------------------------------------------------------------*/ +#define PCI_FUNCTION_ID 0xB1 +#define PCI_BIOS_PRESENT 0x01 +#define FIND_PCI_DEVICE 0x02 +#define FIND_PCI_CLASS_CODE 0x03 +#define GENERATE_SPECIAL_CYCLE 0x06 +#define READ_CONFIG_BYTE 0x08 +#define READ_CONFIG_WORD 0x09 +#define READ_CONFIG_DWORD 0x0A +#define WRITE_CONFIG_BYTE 0x0B +#define WRITE_CONFIG_WORD 0x0C +#define WRITE_CONFIG_DWORD 0x0D + +#define SUCCESSFUL 0x00 +#define FUNC_NOT_SUPPORTED 0x81 +#define BAD_VENDOR_ID 0x83 /* Bad vendor ID */ +#define DEVICE_NOT_FOUND 0x86 /* PCI device not found */ +#define BAD_REGISTER_NUMBER 0x87 + +#define MAX_PCI_DEVICES 21 /* Maximum devices supportted */ + +#define MAX_PCI_CHANL 4 + +typedef struct _BIOS32_ENTRY_STRUCTURE { + DWORD Signatures; /* Should be "_32_" */ + DWORD BIOS32Entry; /* 32-bit physical address */ + BYTE Revision; /* Revision level, should be 0 */ + BYTE Length; /* Multiply of 16, should be 1 */ + BYTE CheckSum; /* Checksum of whole structure */ + BYTE Reserved[5]; /* Reserved */ +} BIOS32_ENTRY_STRUCTURE, *PBIOS32_ENTRY_STRUCTURE; + +typedef struct + { + union + { + unsigned int eax; + struct + { + unsigned short ax; + } word; + struct + { + unsigned char al; + unsigned char ah; + } byte; + } eax; + union + { + unsigned int ebx; + struct + { + unsigned short bx; + } word; + struct + { + unsigned char bl; + unsigned char bh; + } byte; + } ebx; + union + { + unsigned int ecx; + struct + { + unsigned short cx; + } word; + struct + { + unsigned char cl; + unsigned char ch; + } byte; + } ecx; + union + { + unsigned int edx; + struct + { + unsigned short dx; + } word; + struct + { + unsigned char dl; + unsigned char dh; + } byte; + } edx; + union + { + unsigned int edi; + struct + { + unsigned short di; + } word; + } edi; + union + { + unsigned int esi; + struct + { + unsigned short si; + } word; + } esi; + } REGS; + +typedef union /* Union define for mechanism 1 */ + { + struct + { + unsigned char RegNum; + unsigned char FcnNum:3; + unsigned char DeviceNum:5; + unsigned char BusNum; + unsigned char Reserved:7; + unsigned char Enable:1; + } sConfigAdr; + unsigned long lConfigAdr; + } CONFIG_ADR; + +typedef union /* Union define for mechanism 2 */ + { + struct + { + unsigned char RegNum; + unsigned char DeviceNum; + unsigned short Reserved; + } sHostAdr; + unsigned long lHostAdr; + } HOST_ADR; + +typedef struct _HCSinfo { + ULONG base; + UCHAR vec; + UCHAR bios; /* High byte of BIOS address */ + USHORT BaseAndBios; /* high byte: pHcsInfo->bios,low byte:pHcsInfo->base */ + } HCSINFO ; + +#define TUL_RD(x,y) (UCHAR)(inb( (int)((ULONG)(x+y)) )) +#define TUL_RDLONG(x,y) (ULONG)(inl((int)((ULONG)(x+y)) )) +#define TUL_WR( adr,data) outb( (UCHAR)(data), (int)(adr)) +#define TUL_WRSHORT(adr,data) outw( (UWORD)(data), (int)(adr)) +#define TUL_WRLONG( adr,data) outl( (ULONG)(data), (int)(adr)) + +#define SCSI_ABORT_SNOOZE 0 +#define SCSI_ABORT_SUCCESS 1 +#define SCSI_ABORT_PENDING 2 +#define SCSI_ABORT_BUSY 3 +#define SCSI_ABORT_NOT_RUNNING 4 +#define SCSI_ABORT_ERROR 5 + +#define SCSI_RESET_SNOOZE 0 +#define SCSI_RESET_PUNT 1 +#define SCSI_RESET_SUCCESS 2 +#define SCSI_RESET_PENDING 3 +#define SCSI_RESET_WAKEUP 4 +#define SCSI_RESET_NOT_RUNNING 5 +#define SCSI_RESET_ERROR 6 + +#define SCSI_RESET_SYNCHRONOUS 0x01 +#define SCSI_RESET_ASYNCHRONOUS 0x02 +#define SCSI_RESET_SUGGEST_BUS_RESET 0x04 +#define SCSI_RESET_SUGGEST_HOST_RESET 0x08 + +#define SCSI_RESET_BUS_RESET 0x100 +#define SCSI_RESET_HOST_RESET 0x200 +#define SCSI_RESET_ACTION 0xff + diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ini9100u.c linux/drivers/scsi/ini9100u.c --- linux.vanilla/drivers/scsi/ini9100u.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/ini9100u.c Wed Dec 9 09:52:32 1998 @@ -0,0 +1,1234 @@ + +/************************************************************************** + * Initio 9100 device driver for Linux. + * + * Copyright (c) 1994-1998 Initio Corporation + * All rights reserved. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ************************************************************************* + * + * DESCRIPTION: + * + * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host + * adapters + * + * 08/06/97 hc - v1.01h + * - Support inic-940 and inic-935 + * 09/26/97 hc - v1.01i + * - Make correction from J.W. Schultz suggestion + * 10/13/97 hc - Support reset function + * 10/21/97 hc - v1.01j + * - Support 32 LUN (SCSI 3) + * 01/14/98 hc - v1.01k + * - Fix memory allocation problem + * 03/04/98 hc - v1.01l + * - Fix tape rewind which will hang the system problem + * - Set can_queue to tul_num_scb + * 06/25/98 hc - v1.01m + * - Get it work for kernel version >= 2.1.75 + * - Dynamic assign SCSI bus reset holding time in init_tulip() + * 07/02/98 hc - v1.01n + * - Support 0002134A + * 08/07/98 hc - v1.01o + * - Change the tul_abort_srb routine to use scsi_done. <01> + * 09/07/98 hl - v1.02 + * - Change the INI9100U define and proc_dir_entry to + * reflect the newer Kernel 2.1.118, but the v1.o1o + * should work with Kernel 2.1.118. + * 09/20/98 wh - v1.02a + * - Support Abort command. + * - Handle reset routine. + * 09/21/98 hl - v1.03 + * - remove comments. + **************************************************************************/ + +#define CVT_LINUX_VERSION(V,P,S) (V * 65536 + P * 256 + S) + +#ifndef LINUX_VERSION_CODE +#include +#endif + +#ifdef MODULE +#include +#endif + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE <= CVT_LINUX_VERSION(2,1,92) +#include +#endif +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,23) +#include +#endif +#include +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) +#include +#endif +#include "sd.h" +#include "scsi.h" +#include "hosts.h" +#include "ini9100u.h" +#include +#include +#include + +#else + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "../block/blk.h" +#include "scsi.h" +#include "sd.h" +#include "hosts.h" +#include +#include "ini9100u.h" +#endif + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93) +#ifdef CONFIG_PCI +#include +#endif +#endif + +#ifdef DEBUG_i91u +unsigned int i91u_debug = DEBUG_DEFAULT; +#endif + +#ifdef MODULE +Scsi_Host_Template driver_template = INI9100U; +#include "scsi_module.c" +#endif + +char *i91uCopyright = "Copyright (C) 1996-98"; +char *i91uInitioName = "by Initio Corporation"; +char *i91uProductName= "INI-9X00U/UW"; +char *i91uVersion = "v1.03"; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +struct proc_dir_entry proc_scsi_ini9100u = + { + PROC_SCSI_INI9100U , 7, "INI9100U", + S_IFDIR | S_IRUGO | S_IXUGO, 2, + 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; +#endif + +#if 1 /* <01> */ +#define TULSZ(sz) (sizeof(sz) / sizeof(sz[0])) +#define TUL_RDWORD(x,y) (short)(inl((int)((ULONG)((ULONG)x+(UCHAR)y)) )) +#endif + +/* set by i91_setup according to the command line */ +static int setup_called = 0; + +static int tul_num_ch = 4; /* Maximum 4 adapters */ +static int tul_num_scb; +static int tul_tag_enable = 1; +static SCB *tul_scb; + +#ifdef DEBUG_i91u +static int setup_debug = 0; +#endif + +static char *setup_str = (char *)NULL; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr0( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr1( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr2( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr3( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr4( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr5( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr6( int irq, void *dev_id, struct pt_regs * ); +static void i91u_intr7( int irq, void *dev_id, struct pt_regs * ); +#else +static void i91u_intr0( int irq, struct pt_regs * ); +static void i91u_intr1( int irq, struct pt_regs * ); +static void i91u_intr2( int irq, struct pt_regs * ); +static void i91u_intr3( int irq, struct pt_regs * ); +static void i91u_intr4( int irq, struct pt_regs * ); +static void i91u_intr5( int irq, struct pt_regs * ); +static void i91u_intr6( int irq, struct pt_regs * ); +static void i91u_intr7( int irq, struct pt_regs * ); +#endif + +static void i91u_panic(char *msg); + +static void i91uSCBPost(BYTE *pHcb, BYTE *pScb); + + /* ---- EXTERNAL FUNCTIONS ---- */ + /* Get total number of adapters*/ +extern void init_i91uAdapter_table(void); +extern int Addi91u_into_Adapter_table(WORD, WORD, BYTE, BYTE, BYTE); +extern int tul_ReturnNumberOfAdapters(void); +extern void get_tulipPCIConfig(HCS *pHCB, int iChannel_index); +extern int init_tulip(HCS *pHCB, SCB *pSCB, int tul_num_scb, BYTE *pbBiosAdr, int reset_time); +extern SCB *tul_alloc_scb(HCS *pHCB); +extern int tul_abort_srb(HCS *pHCB, Scsi_Cmnd *pSRB); +extern void tul_exec_scb(HCS *pHCB, SCB *pSCB); +extern void tul_release_scb(HCS *pHCB, SCB *pSCB); +extern void tul_stop_bm(HCS *pHCB); +extern int tul_reset_scsi(HCS *pCurHcb, int seconds); +extern int tul_isr(HCS *pHCB); +extern int tul_reset(HCS *pHCB, Scsi_Cmnd *pSRB, unsigned char target); +extern int tul_reset_scsi_bus(HCS *pCurHcb); +extern int tul_device_reset(HCS *pCurHcb, ULONG pSrb, unsigned int target, unsigned int ResetFlags); + /* ---- EXTERNAL VARIABLES ---- */ +extern HCS tul_hcs[]; + +/* + * queue services: + */ +/***************************************************************************** + Function name : i91uAppendSRBToQueue + Description : This function will push current request into save list + Input : pSRB - Pointer to SCSI request block. + pHCB - Pointer to host adapter structure + Output : None. + Return : None. +*****************************************************************************/ +static void i91uAppendSRBToQueue(HCS *pHCB, Scsi_Cmnd *pSRB) +{ + ULONG flags; + + save_flags(flags); + cli(); + + pSRB->next = NULL; /* Pointer to next */ + + if (pHCB->pSRB_head == NULL) + pHCB->pSRB_head = pSRB; + else + pHCB->pSRB_tail->next = pSRB; /* Pointer to next */ + pHCB->pSRB_tail = pSRB; + + restore_flags(flags); + + return; +} + +/***************************************************************************** + Function name : i91uPopSRBFromQueue + Description : This function will pop current request from save list + Input : pHCB - Pointer to host adapter structure + Output : None. + Return : pSRB - Pointer to SCSI request block. +*****************************************************************************/ +static Scsi_Cmnd *i91uPopSRBFromQueue(HCS *pHCB) +{ + Scsi_Cmnd *pSRB; + ULONG flags; + + save_flags(flags); + cli(); + + if ((pSRB = pHCB->pSRB_head) != NULL) + { + pHCB->pSRB_head = pHCB->pSRB_head->next; + pSRB->next = NULL; + } + + restore_flags(flags); + + return (pSRB); +} + +/* called from init/main.c */ + +void i91u_setup( char *str, int *ints) +{ + if (setup_called) + i91u_panic("i91u: i91u_setup called twice.\n"); + + setup_called = ints[0]; + setup_str = str; + +#ifdef DEBUG_i91u + setup_debug = ints[0] >= 1 ? ints[1] : DEBUG_DEFAULT; +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93) +int tul_NewReturnNumberOfAdapters(void) +{ + struct pci_dev *pDev = NULL; /* Start from none */ + int iAdapters = 0; + long dRegValue; + WORD wBIOS; + + init_i91uAdapter_table(); + + while ((pDev = pci_find_device(INI_VENDOR_ID,I950_DEVICE_ID,pDev)) != NULL) + { + pci_read_config_dword(pDev, 0x44, (u32 *) &dRegValue); + wBIOS = (UWORD) (dRegValue & 0xFF); + if (((dRegValue & 0xFF00) >> 8) == 0xFF) + dRegValue = 0; + wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8)); + if (Addi91u_into_Adapter_table( wBIOS, + (pDev->base_address[0] & 0xFFFE), + pDev->irq, + pDev->bus->number, + (pDev->devfn >> 3) + ) == 0) + iAdapters++; + } + while ((pDev = pci_find_device(INI_VENDOR_ID,I940_DEVICE_ID,pDev)) != NULL) + { + pci_read_config_dword(pDev, 0x44, (u32 *) &dRegValue); + wBIOS = (UWORD) (dRegValue & 0xFF); + if (((dRegValue & 0xFF00) >> 8) == 0xFF) + dRegValue = 0; + wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8)); + if (Addi91u_into_Adapter_table( wBIOS, + (pDev->base_address[0] & 0xFFFE), + pDev->irq, + pDev->bus->number, + (pDev->devfn >> 3) + ) == 0) + iAdapters++; + } + while ((pDev = pci_find_device(INI_VENDOR_ID,I935_DEVICE_ID,pDev)) != NULL) + { + pci_read_config_dword(pDev, 0x44, (u32 *) &dRegValue); + wBIOS = (UWORD) (dRegValue & 0xFF); + if (((dRegValue & 0xFF00) >> 8) == 0xFF) + dRegValue = 0; + wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8)); + if (Addi91u_into_Adapter_table( wBIOS, + (pDev->base_address[0] & 0xFFFE), + pDev->irq, + pDev->bus->number, + (pDev->devfn >> 3) + ) == 0) + iAdapters++; + } + while ((pDev = pci_find_device(INI_VENDOR_ID,0x0002,pDev)) != NULL) + { + pci_read_config_dword(pDev, 0x44, (u32 *) &dRegValue); + wBIOS = (UWORD) (dRegValue & 0xFF); + if (((dRegValue & 0xFF00) >> 8) == 0xFF) + dRegValue = 0; + wBIOS = (wBIOS << 8) + ((UWORD) ((dRegValue & 0xFF00) >> 8)); + if (Addi91u_into_Adapter_table( wBIOS, + (pDev->base_address[0] & 0xFFFE), + pDev->irq, + pDev->bus->number, + (pDev->devfn >> 3) + ) == 0) + iAdapters++; + } + + return (iAdapters); +} + +#else /* <01> */ + +/***************************************************************************** + Function name : tul_ReturnNumberOfAdapters + Description : This function will scan PCI bus to get all Orchid card + Input : None. + Output : None. + Return : SUCCESSFUL - Successful scan + ohterwise - No drives founded +*****************************************************************************/ +int tul_ReturnNumberOfAdapters(void) +{ + unsigned int i, iAdapters; + unsigned int dRegValue; + unsigned short command; + WORD wBIOS, wBASE; + BYTE bPCIBusNum, bInterrupt, bPCIDeviceNum; + struct + { + unsigned short vendor_id; + unsigned short device_id; + } const i91u_pci_devices[] = { + {INI_VENDOR_ID, I935_DEVICE_ID}, + {INI_VENDOR_ID, I940_DEVICE_ID}, + {INI_VENDOR_ID, I950_DEVICE_ID}, + {INI_VENDOR_ID, I920_DEVICE_ID} + }; + + + iAdapters = 0; + /* + * PCI-bus probe. + */ + if (pcibios_present()) + { +#ifdef MMAPIO + unsigned long page_offset, base; +#endif + +#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92) + struct pci_dev *pdev = NULL; +#else + int index; + unsigned char pci_bus, pci_devfn; +#endif + + bPCIBusNum = 0; + bPCIDeviceNum = 0; + init_i91uAdapter_table(); + for (i = 0; i < TULSZ(i91u_pci_devices); i++) + { +#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92) + pdev = NULL; + while ((pdev = pci_find_device(i91u_pci_devices[i].vendor_id, + i91u_pci_devices[i].device_id, + pdev))) +#else + index = 0; + while (!(pcibios_find_device(i91u_pci_devices[i].vendor_id, + i91u_pci_devices[i].device_id, + index++, &pci_bus, &pci_devfn)) ) +#endif + { + if ( i == 0 ) + { + /* + printk("i91u: The RAID controller is not supported by\n"); + printk("i91u: this driver, we are ignoring it.\n"); + */ + } + else + { + /* + * Read sundry information from PCI BIOS. + */ +#if LINUX_VERSION_CODE > CVT_LINUX_VERSION(2,1,92) + bPCIBusNum = pdev->bus->number; + bPCIDeviceNum = pdev->devfn; + dRegValue = pdev->base_address[0]; + if (dRegValue == -1) + { /* Check return code */ + printk("\n\ri91u: tulip read configuration error.\n"); + return(0); /* Read configuration space error */ + } + /* <02> read from base address + 0x50 offset to get the wBIOS balue. */ + wBASE = (WORD) dRegValue; + + /* Now read the interrupt line */ + dRegValue = pdev->irq; + bInterrupt = dRegValue & 0xFF;/* Assign interrupt line */ + pci_read_config_word(pdev, PCI_COMMAND, &command); + pci_write_config_word(pdev, PCI_COMMAND, + command | PCI_COMMAND_MASTER | PCI_COMMAND_IO); + +#else + bPCIBusNum = pci_bus; + bPCIDeviceNum = pci_devfn; + pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0, + &dRegValue); + if (dRegValue == -1) + { /* Check return code */ + printk("\n\ri91u: tulip read configuration error.\n"); + return(0); /* Read configuration space error */ + } + /* <02> read from base address + 0x50 offset to get the wBIOS balue. */ + wBASE = (WORD) dRegValue; + + /* Now read the interrupt line */ + pcibios_read_config_dword(pci_bus, pci_devfn, PCI_INTERRUPT_LINE, + &dRegValue); + bInterrupt = dRegValue & 0xFF;/* Assign interrupt line */ + pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command); + pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, + command | PCI_COMMAND_MASTER | PCI_COMMAND_IO); +#endif + wBASE &= PCI_BASE_ADDRESS_IO_MASK; + wBIOS = TUL_RDWORD(wBASE, 0x50); + +#ifdef MMAPIO + base = wBASE & PAGE_MASK; + page_offset = wBASE - base; + + /* + * replace the next line with this one if you are using 2.1.x: + * temp_p->maddr = ioremap(base, page_offset + 256); + */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,0) + wBASE = ioremap(base, page_offset + 256); +#else + wBASE = (WORD)vremap(base, page_offset + 256); +#endif + if(wBASE) + { + wBASE += page_offset; + } +#endif + + if (Addi91u_into_Adapter_table(wBIOS, wBASE, bInterrupt, bPCIBusNum, + bPCIDeviceNum) == 0x0) + iAdapters++; + } + } /* while(pdev=....) */ + } /* for PCI_DEVICES */ + } /* PCI BIOS present */ + return (iAdapters); +} +#endif + +int i91u_detect(Scsi_Host_Template * tpnt) +{ + SCB *pSCB; + HCS *pHCB; + struct Scsi_Host *hreg; + unsigned long i; /* 01/14/98 */ + int ok = 0, iAdapters; + ULONG dBiosAdr; + BYTE *pbBiosAdr; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + tpnt->proc_dir = &proc_scsi_ini9100u; +#endif + + if (setup_called) + { /* Setup by i91u_setup */ + printk("i91u: processing commandline: "); + +#ifdef DEBUG_i91u + if (setup_called > 1) + { + printk("\ni91u: %s\n", setup_str ); + printk("i91u: usage: i91u[=]\n"); + i91u_panic("i91u panics in line %d", __LINE__); + } + i91u_debug = setup_debug; +#endif + } + + /* Get total number of adapters in the motherboard */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,93) + #ifdef CONFIG_PCI + iAdapters = tul_NewReturnNumberOfAdapters(); + #else + iAdapters = tul_ReturnNumberOfAdapters(); + #endif +#else + iAdapters = tul_ReturnNumberOfAdapters(); +#endif + + if (iAdapters == 0) /* If no tulip founded, return*/ + return (0); + + tul_num_ch = (iAdapters > tul_num_ch)? tul_num_ch : iAdapters; + /* Update actually channel number */ + if (tul_tag_enable) /* 1.01i */ + { + tul_num_scb = MAX_TARGETS * i91u_MAXQUEUE; + } + else + { + tul_num_scb = MAX_TARGETS + 3; /* 1-tape, 1-CD_ROM, 1- extra */ + } /* Update actually SCBs per adapter */ + + /* Get total memory needed for HCS */ + i = tul_num_ch * sizeof(HCS); + memset((unsigned char *) &tul_hcs[0], 0, i);/* Initialize tul_hcs 0 */ + /* Get total memory needed for SCB */ + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + for (;tul_num_scb >= MAX_TARGETS+3; tul_num_scb--) + { + i = tul_num_ch * tul_num_scb * sizeof(SCB); + if ((tul_scb = (SCB *) kmalloc(i, GFP_ATOMIC|GFP_DMA)) != NULL) + break; + } +#else + i = tul_num_ch * tul_num_scb * sizeof(SCB); + tul_scb = (SCB *) scsi_init_malloc(i, GFP_ATOMIC|GFP_DMA); +#endif + if (tul_scb == NULL) + { + printk("i91u: SCB memory allocation error\n"); + return (0); + } + + memset((unsigned char *) tul_scb, 0, i); + + pSCB = tul_scb; + for ( i = 0; i < tul_num_ch * tul_num_scb; i++, pSCB++) + { +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pSCB->SCB_SGPAddr = (U32) VIRT_TO_BUS (&pSCB->SCB_SGList[0]); +#else + pSCB->SCB_SGPAddr = (U32) (&pSCB->SCB_SGList[0]); +#endif + } + + for (i = 0, pHCB = &tul_hcs[0]; /* Get pointer for control block*/ + i < tul_num_ch; + i++, pHCB++) + { + pHCB->pSRB_head = NULL; /* Initial SRB save queue */ + pHCB->pSRB_tail = NULL; /* Initial SRB save queue */ + + request_region(pHCB->HCS_Base, 0x100, "i91u"); /* Register */ + + get_tulipPCIConfig(pHCB, i); + + dBiosAdr = pHCB->HCS_BIOS; + dBiosAdr = (dBiosAdr << 4); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pbBiosAdr = phys_to_virt (dBiosAdr); +#endif + + init_tulip(pHCB, tul_scb + (i * tul_num_scb), tul_num_scb, pbBiosAdr, 10); + pHCB->HCS_Index = i; /* 7/29/98 */ + hreg = scsi_register(tpnt, sizeof(HCS)); + hreg->io_port = pHCB->HCS_Base; + hreg->n_io_port = 0xff; + hreg->can_queue = tul_num_scb; /* 03/05/98 */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + hreg->unique_id = pHCB->HCS_Base; + hreg->max_id = pHCB->HCS_MaxTar; +#endif + hreg->max_lun = 32; /* 10/21/97 */ +/* hreg->max_lun = 8; + hreg->max_channel = 1;*/ + hreg->irq = pHCB->HCS_Intr; + hreg->this_id = pHCB->HCS_SCSI_ID;/* Assign HCS index */ + hreg->base = (UCHAR *) pHCB; +#if 1 + hreg->sg_tablesize = TOTAL_SG_ENTRY; /* Maximun support is 32*/ +#else + hreg->sg_tablesize = SG_NONE; /* No SG */ +#endif +/* hreg->this_id = i; Assign HCS index */ + + /* Initial tulip chip */ + switch (i) { +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + case 0: + ok = request_irq(pHCB->HCS_Intr, i91u_intr0,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 1: + ok = request_irq(pHCB->HCS_Intr, i91u_intr1,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 2: + ok = request_irq(pHCB->HCS_Intr, i91u_intr2,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 3: + ok = request_irq(pHCB->HCS_Intr, i91u_intr3,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 4: + ok = request_irq(pHCB->HCS_Intr, i91u_intr4,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 5: + ok = request_irq(pHCB->HCS_Intr, i91u_intr5,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 6: + ok = request_irq(pHCB->HCS_Intr, i91u_intr6,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + case 7: + ok = request_irq(pHCB->HCS_Intr, i91u_intr7,SA_INTERRUPT | SA_SHIRQ,"i91u",NULL); + break; + default: + i91u_panic("i91u: Too many host adapters\n"); + break; + } + if (ok < 0) + { + if (ok == -EINVAL) + { + printk("i91u: bad IRQ %d.\n", pHCB->HCS_Intr); + printk(" Contact author.\n"); + } + else + if( ok == -EBUSY) + printk( "i91u: IRQ %d already in use. Configure another.\n", + pHCB->HCS_Intr); + else + { + printk( "\ni91u: Unexpected error code on requesting IRQ %d.\n", + pHCB->HCS_Intr); + printk(" Contact author.\n"); + } + i91u_panic("i91u: driver needs an IRQ.\n"); + } +#endif + } + + tpnt->this_id = -1; +/* tpnt->can_queue=i91u_MAXQUEUE;*/ + tpnt->can_queue=1; + + return 1; +} + +static void i91uBuildSCB(HCS *pHCB, SCB *pSCB, Scsi_Cmnd *SCpnt) +{ /* Create corresponding SCB */ + struct scatterlist *pSrbSG; + SG *pSG; /* Pointer to SG list */ + int i; + long TotalLen; + + pSCB->SCB_Post = i91uSCBPost; /* i91u's callback routine */ + pSCB->SCB_Srb = SCpnt; + pSCB->SCB_Opcode = ExecSCSI; +#if 0 + pSCB->SCB_Flags = SCF_NO_DCHK; /* Clear done bit */ + pSCB->SCB_Flags |= SCF_POST; /* After SCSI done, call post routine*/ +#else + pSCB->SCB_Flags = SCF_POST; /* After SCSI done, call post routine*/ +#endif + pSCB->SCB_Target = SCpnt->target; + pSCB->SCB_Lun = SCpnt->lun; +/* pSCB->SCB_Ident = SCpnt->lun | DISC_NOT_ALLOW;*/ + pSCB->SCB_Ident = SCpnt->lun | DISC_ALLOW; +#if 1 + pSCB->SCB_Flags |= SCF_SENSE; /* Turn on auto request sense */ +/* memset((unsigned char *) &SCpnt->sense_buffer[0], 0, SENSE_SIZE); 7/22/98 */ +#endif + +#if 1 /* 7/22/98 */ + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pSCB->SCB_SensePtr = (U32) VIRT_TO_BUS (SCpnt->sense_buffer); +#else + pSCB->SCB_SensePtr = (U32) (SCpnt->sense_buffer); +#endif + +#else + pSCB->SCB_SensePtr = pSCB->SCB_SGPAddr; +#endif + + pSCB->SCB_SenseLen = SENSE_SIZE; + + pSCB->SCB_CDBLen = SCpnt->cmd_len; + pSCB->SCB_HaStat = 0; + pSCB->SCB_TaStat = 0; + memcpy(&pSCB->SCB_CDB[0], &SCpnt->cmnd, SCpnt->cmd_len); + + if (SCpnt->device->tagged_supported) + { /* Tag Support */ + pSCB->SCB_TagMsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */ + } + else + { + pSCB->SCB_TagMsg = 0; /* No tag support */ + } + + if (SCpnt->use_sg) + { + pSrbSG = (struct scatterlist *) SCpnt->request_buffer; + if (SCpnt->use_sg == 1) /* If only one entry in the list*/ + { /* treat it as regular I/O */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS (pSrbSG->address); +#else + pSCB->SCB_BufPtr = (U32) (pSrbSG->address); +#endif + TotalLen = pSrbSG->length; + pSCB->SCB_SGLen = 0; + } + else + { /* Assign SG physical address */ + pSCB->SCB_BufPtr = pSCB->SCB_SGPAddr; + pSCB->SCB_Flags |= SCF_SG;/* Turn on SG list flag */ + for (i = 0, TotalLen = 0, pSG = &pSCB->SCB_SGList[0];/* 1.01g*/ + i < SCpnt->use_sg; + i++, pSG++, pSrbSG++) + { +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pSG->SG_Ptr = (U32) VIRT_TO_BUS (pSrbSG->address); +#else + pSG->SG_Ptr = (U32) (pSrbSG->address); +#endif + TotalLen += pSG->SG_Len = pSrbSG->length; + } + pSCB->SCB_SGLen = i; + } + pSCB->SCB_BufLen = (SCpnt->request_bufflen > TotalLen) ? + TotalLen : SCpnt->request_bufflen; + } + else /* Non SG */ + { +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) + pSCB->SCB_BufPtr = (U32) VIRT_TO_BUS(SCpnt->request_buffer); +#else + pSCB->SCB_BufPtr = (U32) (SCpnt->request_buffer); +#endif + pSCB->SCB_BufLen = SCpnt->request_bufflen; + pSCB->SCB_SGLen = 0; + } + + return; +} + +/* + * Queue a command and setup interrupts for a free bus. + */ +int i91u_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) +{ + register SCB *pSCB; + HCS *pHCB; /* Point to Host adapter control block*/ + + if (SCpnt->lun > 16) { /* 07/22/98 */ + + SCpnt->result = (DID_TIME_OUT << 16); + done(SCpnt); /* Notify system DONE */ + return (0); + } + + pHCB = (HCS *) SCpnt->host->base; + + SCpnt->scsi_done = done; + /* Get free SCSI control block */ + if ((pSCB = tul_alloc_scb(pHCB)) == NULL) + { + i91uAppendSRBToQueue(pHCB, SCpnt); /* Buffer this request */ +/* printk("i91u_entry: can't allocate SCB\n");*/ + return (0); + } + i91uBuildSCB(pHCB, pSCB, SCpnt); + tul_exec_scb(pHCB, pSCB); /* Start execute SCB */ + return (0); +} + +/* + * We only support command in interrupt-driven fashion + */ +int i91u_command( Scsi_Cmnd *SCpnt ) +{ + printk( "i91u: interrupt driven driver; use i91u_queue()\n" ); + return -1; +} + +/* + * Abort a queued command + * (commands that are on the bus can't be aborted easily) + */ +int i91u_abort( Scsi_Cmnd *SCpnt) +{ + HCS *pHCB; + + pHCB = (HCS *) SCpnt->host->base; + return tul_abort_srb(pHCB, SCpnt); +} + +/* + * Reset registers, reset a hanging bus and + * kill active and disconnected commands for target w/o soft reset + */ +int i91u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) +{ /* I need Host Control Block Information */ + HCS *pHCB; + + pHCB = (HCS *) SCpnt->host->base; + + if (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET|SCSI_RESET_SUGGEST_HOST_RESET)) + + return tul_reset_scsi_bus(pHCB); + else + return tul_device_reset(pHCB, (ULONG)SCpnt, SCpnt->target, reset_flags); +} + +/* + * Return the "logical geometry" + */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +int i91u_biosparam(Scsi_Disk *disk, kdev_t dev, int *info_array ) +#else +int i91u_biosparam(Scsi_Disk *disk, int dev, int *info_array ) +#endif +{ + HCS *pHcb; /* Point to Host adapter control block*/ + TCS *pTcb; + + pHcb = (HCS *) disk->device->host->base; + pTcb = &pHcb->HCS_Tcs[disk->device->id]; + + if (pTcb->TCS_DrvHead) + { + info_array[0] = pTcb->TCS_DrvHead; + info_array[1] = pTcb->TCS_DrvSector; + info_array[2] = disk->capacity / pTcb->TCS_DrvHead / pTcb->TCS_DrvSector; + } + else + { + if (pTcb->TCS_DrvFlags & TCF_DRV_255_63) + { + info_array[0] = 255; + info_array[1] = 63; + info_array[2] = disk->capacity / 255 / 63; + } + else + { + info_array[0] = 64; + info_array[1] = 32; + info_array[2] = disk->capacity >> 11; + } + } + +#if defined(DEBUG_BIOSPARAM) + if (i91u_debug & debug_biosparam) + { + printk("bios geometry: head=%d, sec=%d, cyl=%d\n", + info_array[0], info_array[1], info_array[2]); + printk("WARNING: check, if the bios geometry is correct.\n"); + } +#endif + + return 0; +} + +/***************************************************************************** + Function name : i91uSCBPost + Description : This is callback routine be called when tulip finish one + SCSI command. + Input : pHCB - Pointer to host adapter control block. + pSCB - Pointer to SCSI control block. + Output : None. + Return : None. +*****************************************************************************/ +static void i91uSCBPost(BYTE *pHcb, BYTE *pScb) +{ + Scsi_Cmnd *pSRB; /* Pointer to SCSI request block */ + HCS *pHCB; + SCB *pSCB; + + pHCB = (HCS *)pHcb; + pSCB = (SCB *)pScb; + if ((pSRB = pSCB->SCB_Srb) == 0) + { + printk("i91uSCBPost: SRB pointer is empty\n"); + + tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */ + return; + } + switch (pSCB->SCB_HaStat) { + case 0x0: + case 0xa: /* Linked command complete without error and linked normally*/ + case 0xb: /* Linked command complete without error interrupt generated*/ + pSCB->SCB_HaStat = 0; + break; + + case 0x11: /* Selection time out-The initiator selection or target + reselection was not complete within the SCSI Time out period */ + pSCB->SCB_HaStat = DID_TIME_OUT; + break; + + case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus + phase sequence was requested by the target. The host adapter + will generate a SCSI Reset Condition, notifying the host with + a SCRD interrupt */ + pSCB->SCB_HaStat = DID_RESET; + break; + + case 0x1a: /* SCB Aborted. 07/21/98 */ + pSCB->SCB_HaStat = DID_ABORT; + break; + + case 0x12: /* Data overrun/underrun-The target attempted to transfer more data + than was allocated by the Data Length field or the sum of the + Scatter / Gather Data Length fields. */ + case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */ + case 0x16: /* Invalid SCB Operation Code. */ + + default: + printk("ini9100u: %x %x\n", pSCB->SCB_HaStat, pSCB->SCB_TaStat); + pSCB->SCB_HaStat = DID_ERROR; /* Couldn't find any better */ + break; + } + +#if 0 + if ((pSCB->SCB_TaStat == 2) && /* Check condition */ + (pSCB->SCB_Flags & SCF_SENSE)) /* Turn on auto request sense */ + { + memcpy((unsigned char *) &pSRB->sense_buffer[0], (unsigned char *) &pSCB->SCB_SGList[0], SENSE_SIZE); + } +#endif + pSRB->result = pSCB->SCB_TaStat | (pSCB->SCB_HaStat << 16); + + if (pSRB == NULL) + { + printk("pSRB is NULL\n"); + } + pSRB->scsi_done(pSRB); /* Notify system DONE */ + if ((pSRB = i91uPopSRBFromQueue(pHCB)) != NULL) + /* Find the next pending SRB */ + { /* Assume resend will success */ + /* Reuse old SCB */ + i91uBuildSCB(pHCB, pSCB, pSRB); /* Create corresponding SCB */ + + tul_exec_scb(pHCB, pSCB); /* Start execute SCB */ + } + else + { /* No Pending SRB */ + tul_release_scb(pHCB, pSCB); /* Release SCB for current channel */ + } + return; +} + +/* + * Interrupts handler (main routine of the driver) + */ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr0( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr0( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[0].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[0]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr1( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr1( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[1].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[1]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr2( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr2( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[2].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[2]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr3( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr3( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[3].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[3]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr4( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr4( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[4].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[4]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr5( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr5( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[5].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[5]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr6( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr6( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[6].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[6]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1,3,0) +static void i91u_intr7( int irqno, void *dev_id, struct pt_regs * regs ) +#else +static void i91u_intr7( int irqno, struct pt_regs * regs ) +#endif +{ +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + unsigned long flags; +#endif + + if (tul_hcs[7].HCS_Intr != irqno) + return; + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_lock_irqsave(&io_request_lock, flags); +#endif + + tul_isr(&tul_hcs[7]); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(2,1,95) + spin_unlock_irqrestore(&io_request_lock, flags); +#endif +} + +/* + * Dump the current driver status and panic... + */ +static void i91u_panic(char *msg) +{ + printk("\ni91u_panic: %s\n", msg); + panic("i91u panic"); +} +/*#include "i91uscsi.c"*/ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/ini9100u.h linux/drivers/scsi/ini9100u.h --- linux.vanilla/drivers/scsi/ini9100u.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/scsi/ini9100u.h Fri Dec 11 21:05:15 1998 @@ -0,0 +1,327 @@ + +/************************************************************************** + * Initio 9100 device driver for Linux. + * + * Copyright (c) 1994-1998 Initio Corporation + * All rights reserved. + * + * 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Where this Software is combined with software released under the terms of + * the GNU Public License ("GPL") and the terms of the GPL would require the + * combined work to also be released under the terms of the GPL, the terms + * and conditions of this License will apply in addition to those of the + * GPL with the exception of any terms or conditions of this License that + * conflict with, or are expressly prohibited by, the GPL. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + ************************************************************************* + * + * Module: ini9100u.h + * Description: INI-9100U/UW LINUX device driver header + * Revision History: + * 06/18/96 Harry Chen, Initial Version 1.00A (Beta) + * 06/23/98 hc - v1.01k + * - Get it work for kernel version >= 2.1.75 +*******************************************************************************/ + +#ifndef CVT_LINUX_VERSION +#define CVT_LINUX_VERSION(V,P,S) (((V) * 65536) + ((P) * 256) + (S)) +#endif + +#ifndef LINUX_VERSION_CODE +#include +#endif + +#include "sd.h" + +extern int i91u_detect(Scsi_Host_Template *); +extern int i91u_command(Scsi_Cmnd *); +extern int i91u_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int i91u_abort(Scsi_Cmnd *); +extern int i91u_reset(Scsi_Cmnd *, unsigned int); + +#if LINUX_VERSION_CODE >= CVT_LINUX_VERSION(1, 3, 0) +extern int i91u_biosparam(Scsi_Disk *, kdev_t, int *); /*for linux v2.0*/ +extern struct proc_dir_entry proc_scsi_ini9100u; +#else +extern int i91u_biosparam(Disk *, int, int *); /*for linux v1.13 */ +#endif + +#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.03" + +#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(1, 3, 0) +#define INI9100U { \ + NULL, \ + NULL, \ + i91u_REVID, \ + i91u_detect, \ + NULL, \ + NULL, \ + i91u_command, \ + i91u_queue, \ + i91u_abort, \ + i91u_reset, \ + NULL, \ + i91u_biosparam, \ + 1, \ + 7, \ + SG_ALL, \ + 1, \ + 0, \ + 0, \ + ENABLE_CLUSTERING \ +} +#else + +#if LINUX_VERSION_CODE < CVT_LINUX_VERSION(2, 1, 75) +#define INI9100U { \ + NULL, \ + NULL, \ + &proc_scsi_ini9100u, \ + NULL, \ + i91u_REVID, \ + i91u_detect, \ + NULL, \ + NULL, \ + i91u_command, \ + i91u_queue, \ + i91u_abort, \ + i91u_reset, \ + NULL, \ + i91u_biosparam, \ + 1, \ + 7, \ + SG_ALL, \ + 1, \ + 0, \ + 0, \ + ENABLE_CLUSTERING \ +} +#else /* Version >= 2.1.75 */ +#define INI9100U { \ + next: NULL, \ + module: NULL, \ + proc_dir: &proc_scsi_ini9100u, \ + proc_info: NULL, \ + name: i91u_REVID, \ + detect: i91u_detect, \ + release: NULL, \ + info: NULL, \ + command: i91u_command, \ + queuecommand: i91u_queue, \ + eh_strategy_handler: NULL, \ + eh_abort_handler: NULL, \ + eh_device_reset_handler: NULL, \ + eh_bus_reset_handler: NULL, \ + eh_host_reset_handler: NULL, \ + abort: i91u_abort, \ + reset: i91u_reset, \ + slave_attach: NULL, \ + bios_param: i91u_biosparam, \ + can_queue: 1, \ + this_id: 1, \ + sg_tablesize: SG_ALL, \ + cmd_per_lun: 1, \ + present: 0, \ + unchecked_isa_dma: 0, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 0 \ +} +#endif +#endif + + +#define VIRT_TO_BUS(i) (unsigned int) virt_to_bus((void *)(i)) +#define ULONG unsigned long +#define USHORT unsigned short +#define UCHAR unsigned char +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned long +#define UBYTE unsigned char +#define UWORD unsigned short +#define UDWORD unsigned long +#ifdef ALPHA +#define U32 unsigned int +#else +#define U32 unsigned long +#endif + +#ifndef NULL +#define NULL 0 /* zero */ +#endif +#ifndef TRUE +#define TRUE (1) /* boolean true */ +#endif +#ifndef FALSE +#define FALSE (0) /* boolean false */ +#endif +#ifndef FAILURE +#define FAILURE (-1) +#endif + +#define i91u_MAXQUEUE 2 +#define TOTAL_SG_ENTRY 32 +#define MAX_TARGETS 16 +#define SENSE_SIZE 14 + +#define INI_VENDOR_ID 0x1101 /* Initio's PCI vendor ID */ +#define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */ +#define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */ +#define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */ +#define I920_DEVICE_ID 0x0002 /* Initio's other product ID */ + +/************************************************************************/ +/* Scatter-Gather Element Structure */ +/************************************************************************/ +typedef struct SG_Struc { + U32 SG_Ptr; /* Data Pointer */ + U32 SG_Len; /* Data Length */ +} SG; + +/*********************************************************************** + SCSI Control Block +************************************************************************/ +typedef struct Scsi_Ctrl_Blk { + U32 SCB_InitioReserved[9]; /* 0 */ + + UBYTE SCB_Opcode; /*24 SCB command code */ + UBYTE SCB_Flags; /*25 SCB Flags */ + UBYTE SCB_Target; /*26 Target Id */ + UBYTE SCB_Lun; /*27 Lun */ + U32 SCB_BufPtr; /*28 Data Buffer Pointer */ + U32 SCB_BufLen; /*2C Data Allocation Length */ + UBYTE SCB_SGLen; /*30 SG list # */ + UBYTE SCB_SenseLen; /*31 Sense Allocation Length */ + UBYTE SCB_HaStat; /*32 */ + UBYTE SCB_TaStat; /*33 */ + UBYTE SCB_CDBLen; /*34 CDB Length */ + UBYTE SCB_Ident; /*35 Identify */ + UBYTE SCB_TagMsg; /*36 Tag Message */ + UBYTE SCB_TagId; /*37 Queue Tag */ + UBYTE SCB_CDB[12]; /*38 */ + U32 SCB_SGPAddr; /*44 SG List/Sense Buf phy. Addr. */ + U32 SCB_SensePtr; /*48 Sense data pointer */ + void (* SCB_Post)(BYTE *, BYTE *); /*4C POST routine */ + Scsi_Cmnd *SCB_Srb; /*50 SRB Pointer */ + SG SCB_SGList[TOTAL_SG_ENTRY]; /*54 Start of SG list */ +} SCB; + +/* Opcodes of SCB_Opcode */ +#define ExecSCSI 0x1 +#define BusDevRst 0x2 +#define AbortCmd 0x3 + +/* Bit Definition for SCB_Flags */ +#define SCF_DONE 0x01 +#define SCF_POST 0x02 +#define SCF_SENSE 0x04 +#define SCF_DIR 0x18 +#define SCF_NO_DCHK 0x00 +#define SCF_DIN 0x08 +#define SCF_DOUT 0x10 +#define SCF_NO_XF 0x18 +#define SCF_POLL 0x40 +#define SCF_SG 0x80 + +/* Error Codes for SCB_HaStat */ +#define HOST_SEL_TOUT 0x11 +#define HOST_DO_DU 0x12 +#define HOST_BUS_FREE 0x13 +#define HOST_BAD_PHAS 0x14 +#define HOST_INV_CMD 0x16 +#define HOST_SCSI_RST 0x1B +#define HOST_DEV_RST 0x1C + +/* Error Codes for SCB_TaStat */ +#define TARGET_CHKCOND 0x02 +#define TARGET_BUSY 0x08 + +/* Queue tag msg: Simple_quque_tag, Head_of_queue_tag, Ordered_queue_tag */ +#define MSG_STAG 0x20 +#define MSG_HTAG 0x21 +#define MSG_OTAG 0x22 + +/*********************************************************************** + Target Device Control Structure +**********************************************************************/ + +typedef struct Tar_Ctrl_Struc { + ULONG TCS_InitioReserved; /* 0 */ + + UWORD TCS_DrvFlags; /* 4 */ + UBYTE TCS_DrvHead; /* 6 */ + UBYTE TCS_DrvSector; /* 7 */ +} TCS; + +/*********************************************************************** + Target Device Control Structure +**********************************************************************/ +/* Bit Definition for TCF_DrvFlags */ +#define TCF_DRV_255_63 0x0400 + +/*********************************************************************** + Host Adapter Control Structure +************************************************************************/ +typedef struct Ha_Ctrl_Struc { + UWORD HCS_Base; /* 00 */ + UWORD HCS_BIOS; /* 02 */ + UBYTE HCS_Intr; /* 04 */ + UBYTE HCS_SCSI_ID; /* 05 */ + UBYTE HCS_MaxTar; /* 06 */ + UBYTE HCS_NumScbs; /* 07 */ + + UBYTE HCS_Flags; /* 08 */ + UBYTE HCS_Index; /* 09 */ + UBYTE HCS_Reserved[2]; /* 0a */ + ULONG HCS_InitioReserved[27]; /* 0C */ + + TCS HCS_Tcs[16]; /* 78 -> 16 Targets */ + Scsi_Cmnd *pSRB_head; /* SRB save queue header */ + Scsi_Cmnd *pSRB_tail; /* SRB save queue tail */ +} HCS; + +/* Bit Definition for HCB_Flags */ +#define HCF_EXPECT_RESET 0x10 + +/* SCSI related definition */ +#define DISC_NOT_ALLOW 0x80 /* Disconnect is not allowed */ +#define DISC_ALLOW 0xC0 /* Disconnect is allowed */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- linux.vanilla/drivers/scsi/scsi.c Sun Dec 6 00:14:44 1998 +++ linux/drivers/scsi/scsi.c Sun Dec 6 03:17:45 1998 @@ -288,6 +288,7 @@ {"PIONEER","CD-ROM DRM-600","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, +{"PIONEER","CD-ROM DRM-1804X","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, {"CANON","IPUBJD","*", BLIST_SPARSELUN}, {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN}, diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/scsiiom.c linux/drivers/scsi/scsiiom.c --- linux.vanilla/drivers/scsi/scsiiom.c Sun Jun 21 18:41:18 1998 +++ linux/drivers/scsi/scsiiom.c Sun Dec 6 03:15:09 1998 @@ -4,203 +4,264 @@ * Description: Device Driver for Tekram DC-390 (T) PCI SCSI * * Bus Master Host Adapter * ***********************************************************************/ +/* $Id: scsiiom.c,v 2.4 1998/11/05 10:16:43 garloff Exp $ */ - -static USHORT -DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) +UCHAR +dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ) { - USHORT ioport, rc; - UCHAR bval, bval1, i, cnt; - PUCHAR ptr; - ULONG wlval; + USHORT wlval; + UCHAR bval, bval1; pSRB->TagNumber = 31; - ioport = pACB->IOPortBase; - bval = pDCB->UnitSCSIID; - outb(bval,ioport+Scsi_Dest_ID); - bval = pDCB->SyncPeriod; - outb(bval,ioport+Sync_Period); - bval = pDCB->SyncOffset; - outb(bval,ioport+Sync_Offset); - bval = pDCB->CtrlR1; - outb(bval,ioport+CtrlReg1); - bval = pDCB->CtrlR3; - outb(bval,ioport+CtrlReg3); - bval = pDCB->CtrlR4; - outb(bval,ioport+CtrlReg4); - bval = CLEAR_FIFO_CMD; /* Flush FIFO */ - outb(bval,ioport+ScsiCmd); - + DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID); + DC390_write8 (Sync_Period, pDCB->SyncPeriod); + DC390_write8 (Sync_Offset, pDCB->SyncOffset); + DC390_write8 (CtrlReg1, pDCB->CtrlR1); + DC390_write8 (CtrlReg3, pDCB->CtrlR3); + DC390_write8 (CtrlReg4, pDCB->CtrlR4); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); /* Flush FIFO */ + DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\ + pSRB->CmdBlock[0], pDCB->SyncMode);) pSRB->ScsiPhase = SCSI_NOP0; + //pSRB->MsgOutBuf[0] = MSG_NOP; + //pSRB->MsgCnt = 0; bval = pDCB->IdentifyMsg; - if( !(pDCB->SyncMode & EN_ATN_STOP) ) + if( !(pDCB->SyncMode & EN_ATN_STOP) ) /* Don't always try send Extended messages on arbitration */ { if( (pSRB->CmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) ) { - bval &= 0xBF; /* NO disconnection */ - outb(bval,ioport+ScsiFifo); - bval1 = SELECT_W_ATN; + bval &= 0xBF; /* No DisConn */ + DC390_write8 (ScsiFifo, bval); + bval1 = SEL_W_ATN; pSRB->SRBState = SRB_START_; + DEBUG1(printk ("DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);) if( pDCB->SyncMode & SYNC_ENABLE ) - { - if( !(pDCB->IdentifyMsg & 7) || + { + if( !(pDCB->IdentifyMsg & 7) || /* LUN == 0 || Cmd != INQUIRY */ (pSRB->CmdBlock[0] != INQUIRY) ) { - bval1 = SEL_W_ATN_STOP; + bval1 = SEL_W_ATN_STOP; /* Try to establish SYNC nego */ pSRB->SRBState = SRB_MSGOUT; } } } - else + else /* TagQ ? */ { - if(pDCB->SyncMode & EN_TAG_QUEUING) + DC390_write8 (ScsiFifo, bval); + if(pDCB->SyncMode & EN_TAG_QUEUEING) { - outb(bval,ioport+ScsiFifo); - bval = MSG_SIMPLE_QTAG; - outb(bval,ioport+ScsiFifo); - wlval = 1; - bval = 0; - while( wlval & pDCB->TagMask ) - { - wlval = wlval << 1; - bval++; - } - outb(bval,ioport+ScsiFifo); + DC390_write8 (ScsiFifo, MSG_SIMPLE_QTAG); + DEBUG1(printk ("DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN3, pDCB->TagMask);) + bval = 0; wlval = 1; + while (wlval & pDCB->TagMask) + { bval++; wlval <<= 1; }; pDCB->TagMask |= wlval; + DC390_write8 (ScsiFifo, bval); pSRB->TagNumber = bval; - bval1 = SEL_W_ATN2; + DEBUG1(printk ("DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);) + bval1 = SEL_W_ATN3; pSRB->SRBState = SRB_START_; } - else + else /* No TagQ */ { - outb(bval,ioport+ScsiFifo); - bval1 = SELECT_W_ATN; + bval1 = SEL_W_ATN; + DEBUG1(printk ("DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);) pSRB->SRBState = SRB_START_; } } - if( pSRB->SRBFlag & AUTO_REQSENSE ) - { - bval = REQUEST_SENSE; - outb(bval,ioport+ScsiFifo); - bval = pDCB->IdentifyMsg << 5; - outb(bval,ioport+ScsiFifo); - bval = 0; - outb(bval,ioport+ScsiFifo); - outb(bval,ioport+ScsiFifo); - bval = sizeof(pSRB->pcmd->sense_buffer); - outb(bval,ioport+ScsiFifo); - bval = 0; - outb(bval,ioport+ScsiFifo); - } - else - { - cnt = pSRB->ScsiCmdLen; - ptr = (PUCHAR) pSRB->CmdBlock; - for(i=0; iCmdBlock[0] == INQUIRY) || (pSRB->CmdBlock[0] == REQUEST_SENSE) || (pSRB->SRBFlag & AUTO_REQSENSE) ) { - bval &= 0xBF; - outb(bval,ioport+ScsiFifo); - bval1 = SELECT_W_ATN; + bval &= 0xBF; /* No DisConn */ + DC390_write8 (ScsiFifo, bval); + bval1 = SEL_W_ATN; + DEBUG1(printk ("DC390: No DisCn, No TagQ (%02x, %02x)\n", bval, bval1);) pSRB->SRBState = SRB_START_; + /* ??? */ if( pDCB->SyncMode & SYNC_ENABLE ) { - if( !(pDCB->IdentifyMsg & 7) || + if( !(pDCB->IdentifyMsg & 7) || /* LUN == 0 || Cmd != INQUIRY */ (pSRB->CmdBlock[0] != INQUIRY) ) { - bval1 = SEL_W_ATN_STOP; + bval1 = SEL_W_ATN_STOP; /* Try to establish Sync nego */ pSRB->SRBState = SRB_MSGOUT; } } } - else + else /* TagQ ? */ { - if(pDCB->SyncMode & EN_TAG_QUEUING) + DC390_write8 (ScsiFifo, bval); + if(pDCB->SyncMode & EN_TAG_QUEUEING) { - outb(bval,ioport+ScsiFifo); pSRB->MsgOutBuf[0] = MSG_SIMPLE_QTAG; - wlval = 1; - bval = 0; - while( wlval & pDCB->TagMask ) - { - wlval = wlval << 1; - bval++; - } + DEBUG1(printk ("DC390: %sDisCn, TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, SEL_W_ATN_STOP, pDCB->TagMask);) + bval = 0; wlval = 1; + while (wlval & pDCB->TagMask) + { bval++; wlval <<= 1; }; pDCB->TagMask |= wlval; pSRB->TagNumber = bval; + DEBUG1(printk ("DC390: SRB %p (Cmd %li), Tag %02x queued\n", pSRB, pSRB->pcmd->pid, bval);) pSRB->MsgOutBuf[1] = bval; pSRB->MsgCnt = 2; bval1 = SEL_W_ATN_STOP; - pSRB->SRBState = SRB_START_; + pSRB->SRBState = SRB_START_; /* ?? */ } - else + else /* No TagQ */ { - outb(bval,ioport+ScsiFifo); pSRB->MsgOutBuf[0] = MSG_NOP; pSRB->MsgCnt = 1; pSRB->SRBState = SRB_START_; bval1 = SEL_W_ATN_STOP; - } + DEBUG1(printk ("DC390: %sDisCn, No TagQ (%02x, %02x, %08lx)\n", (bval&0x40?"":"No "), bval, bval1, pDCB->TagMask);) + }; } } - bval = inb( ioport+Scsi_Status ); - if( bval & INTERRUPT ) + if (bval1 != SEL_W_ATN_STOP) + { /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */ + if( pSRB->SRBFlag & AUTO_REQSENSE ) + { + bval = 0; + DC390_write8 (ScsiFifo, REQUEST_SENSE); + DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5); + DC390_write8 (ScsiFifo, bval); + DC390_write8 (ScsiFifo, bval); + DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer)); + DC390_write8 (ScsiFifo, bval); + DEBUG1(printk ("DC390: AutoReqSense !\n");) + } + else /* write cmnd to bus */ + { + PUCHAR ptr; UCHAR i; + ptr = (PUCHAR) pSRB->CmdBlock; + for (i=0; iScsiCmdLen; i++) + DC390_write8 (ScsiFifo, *(ptr++)); + }; + } + + /* Check if we can't win arbitration */ + if (DC390_read8 (Scsi_Status) & INTERRUPT) { pSRB->SRBState = SRB_READY; pDCB->TagMask &= ~( 1 << pSRB->TagNumber ); - rc = 1; + DEBUG0(printk ("DC390: Interrupt during StartSCSI!\n");) + return 1; } else { pSRB->ScsiPhase = SCSI_NOP1; + DEBUG0(if (pACB->pActiveDCB) \ + printk ("DC390: ActiveDCB != 0\n");) + DEBUG0(if (pDCB->pActiveSRB) \ + printk ("DC390: ActiveSRB != 0\n");) pACB->pActiveDCB = pDCB; pDCB->pActiveSRB = pSRB; - rc = 0; - outb(bval1,ioport+ScsiCmd); + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + DC390_write8 (ScsiCmd, bval1); + return 0; } - return( rc ); } +//#define DMA_INT EN_DMA_INT /*| EN_PAGE_INT*/ +#define DMA_INT 0 + +#if DMA_INT +/* This is similar to AM53C974.c ... */ +static UCHAR +dc390_dma_intr (PACB pACB) +{ + PSRB pSRB; + UCHAR dstate; + DEBUG0(USHORT pstate;PDEVDECL1;) + + DEBUG0(PDEVSET1;) + DEBUG0(PCI_READ_CONFIG_WORD (PDEV, PCI_STATUS, &pstate);) + DEBUG0(if (pstate & (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY))\ + { printk("DC390: PCI state = %04x!\n", pstate); \ + PCI_WRITE_CONFIG_WORD (PDEV, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY));};) + + dstate = DC390_read8 (DMA_Status); + DC390_write8 (DMA_Status, dstate); /* clear */ + //DC390_write8 (DMA_Status, DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ + + if (! pACB->pActiveDCB || ! pACB->pActiveDCB->pActiveSRB) return dstate; + else pSRB = pACB->pActiveDCB->pActiveSRB; + + if (dstate & (DMA_XFER_ABORT | DMA_XFER_ERROR | POWER_DOWN | PCI_MS_ABORT)) + { + printk (KERN_ERR "DC390: DMA error (%02x)!\n", dstate); + return dstate; + }; + if (dstate & DMA_XFER_DONE) + { + ULONG residual, xferCnt; int ctr = 5000000; + if (! (DC390_read8 (DMA_Cmd) & READ_DIRECTION)) + { + do + { + DEBUG1(printk (KERN_DEBUG "DC390: read residual bytes ... \n");) + dstate = DC390_read8 (DMA_Status); + DC390_write8 (DMA_Status, dstate); /* clear */ + residual = DC390_read8 (CtcReg_Low) | DC390_read8 (CtcReg_Mid) << 8 | + DC390_read8 (CtcReg_High) << 16; + residual += DC390_read8 (Current_Fifo) & 0x1f; + } while (residual && ! (dstate & SCSI_INTERRUPT) && --ctr); + if (!ctr) printk (KERN_CRIT "DC390: dma_intr: DMA aborted unfinished: %06x bytes remain!\n", DC390_read32 (DMA_Wk_ByteCntr)); + /* residual = ... */ + } + else + residual = 0; + + /* ??? */ + + xferCnt = pSRB->SGToBeXferLen - residual; + pSRB->SGBusAddr += xferCnt; + pSRB->TotalXferredLen += xferCnt; + pSRB->SGToBeXferLen = residual; +# ifdef DC390_DEBUG0 + printk (KERN_INFO "DC390: DMA: residual = %i, xfer = %i\n", + (unsigned int)residual, (unsigned int)xferCnt); +# endif + + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + } + return dstate; +}; +#endif -#ifndef VERSION_ELF_1_2_13 -static void +void __inline__ DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) -#else -static void -DC390_Interrupt( int irq, struct pt_regs *regs) -#endif { PACB pACB; PDCB pDCB; PSRB pSRB; - USHORT ioport = 0; - USHORT phase, i; + UCHAR sstatus=0; + UCHAR phase, i; void (*stateV)( PACB, PSRB, PUCHAR ); - UCHAR istate = 0; - UCHAR sstatus=0, istatus; + UCHAR istate, istatus; + UCHAR dstatus; + DC390_AFLAGS DC390_IFLAGS DC390_DFLAGS - pACB = pACB_start; - if( pACB == NULL ) + pACB = dc390_pACB_start; + + if (pACB == 0) + { + printk(KERN_WARNING "DC390: Interrupt on uninitialized adapter!\n"); return; - for( i=0; i < adapterCnt; i++ ) + } + DC390_LOCK_DRV; + + for( i=0; i < dc390_adapterCnt; i++ ) { if( pACB->IRQLevel == (UCHAR) irq ) { - ioport = pACB->IOPortBase; - sstatus = inb( ioport+Scsi_Status ); + sstatus = DC390_read8 (Scsi_Status); if( sstatus & INTERRUPT ) break; else @@ -212,90 +273,128 @@ } } -#ifdef DC390_DEBUG1 - printk("sstatus=%2x,",sstatus); -#endif + DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus);) - if( pACB == (PACB )-1 ) - { - printk("DC390: Spurious interrupt detected!\n"); + if( !pACB ) { DC390_UNLOCK_DRV; return; }; + +#if DMA_INT + DC390_LOCK_IO; + DC390_LOCK_ACB; + dstatus = dc390_dma_intr (pACB); + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; + + DEBUG1(printk ("dstatus=%02x,", dstatus);) + if (! (dstatus & SCSI_INTERRUPT)) + { + DEBUG0(printk ("DC390 Int w/o SCSI actions (only DMA?)\n");) + DC390_UNLOCK_DRV; return; - } - - istate = inb( ioport+Intern_State ); - istatus = inb( ioport+INT_Status ); - -#ifdef DC390_DEBUG1 - printk("Istatus=%2x,",istatus); + }; +#else + dstatus = DC390_read8 (DMA_Status); #endif + DC390_LOCK_IO; + DC390_LOCK_ACB; + DC390_UNLOCK_DRV_NI; /* Allow _other_ CPUs to process IRQ (useful for shared IRQs) */ + + istate = DC390_read8 (Intern_State); + istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */ + + DEBUG1(printk ("Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus);) + dc390_laststatus = dstatus<<24 | sstatus<<16 | istate<<8 | istatus; + + if (sstatus & ILLEGAL_OP_ERR) + { + printk ("DC390: Illegal Operation detected (%08lx)!\n", dc390_laststatus); + dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB); + }; + if(istatus & DISCONNECTED) { - DC390_Disconnect( pACB ); - return; + dc390_Disconnect( pACB ); + goto unlock; } if(istatus & RESELECTED) { - DC390_Reselect( pACB ); - return; + dc390_Reselect( pACB ); + goto unlock; } if(istatus & INVALID_CMD) { - DC390_InvalidCmd( pACB ); - return; + dc390_InvalidCmd( pACB ); + goto unlock; } if(istatus & SCSI_RESET) { - DC390_ScsiRstDetect( pACB ); - return; + dc390_ScsiRstDetect( pACB ); + goto unlock; } - if( istatus & (SUCCESSFUL_OP+SERVICE_REQUEST) ) + if( istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) ) { pDCB = pACB->pActiveDCB; - pSRB = pDCB->pActiveSRB; - if( pDCB ) + if (!pDCB) { - if( pDCB->DCBFlag & ABORT_DEV_ ) - EnableMsgOut( pACB, pSRB ); - } + printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n"); + goto unlock; + }; + pSRB = pDCB->pActiveSRB; + if( pDCB->DCBFlag & ABORT_DEV_ ) + dc390_EnableMsgOut( pACB, pSRB ); - phase = (USHORT) pSRB->ScsiPhase; - stateV = (void *) DC390_phase0[phase]; - stateV( pACB, pSRB, &sstatus ); + phase = pSRB->ScsiPhase; + DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus);) + stateV = (void *) dc390_phase0[phase]; + ( *stateV )( pACB, pSRB, &sstatus ); pSRB->ScsiPhase = sstatus & 7; - phase = (USHORT) sstatus & 7; - stateV = (void *) DC390_phase1[phase]; - stateV( pACB, pSRB, &sstatus ); + phase = (UCHAR) sstatus & 7; + DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus);) + stateV = (void *) dc390_phase1[phase]; + ( *stateV )( pACB, pSRB, &sstatus ); } + unlock: + DC390_LOCK_DRV_NI; + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; + DC390_UNLOCK_DRV; /* Restore initial flags */ } +void +do_DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) +{ + DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq);) + /* Locking is done in DC390_Interrupt */ + DC390_Interrupt(irq, dev_id, regs); + DEBUG1(printk (".. IRQ returned\n");) +} -static void -DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +void +dc390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR sstatus, bval; - USHORT ioport; + UCHAR sstatus; PSGL psgl; ULONG ResidCnt, xferCnt; - ioport = pACB->IOPortBase; sstatus = *psstatus; if( !(pSRB->SRBState & SRB_XFERPAD) ) { - if( sstatus & PARITY_ERR ) + if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) ) pSRB->SRBStatus |= PARITY_ERROR; if( sstatus & COUNT_2_ZERO ) { - bval = inb(ioport+DMA_Status); - while( !(bval & DMA_XFER_DONE) ) - bval = inb(ioport+DMA_Status); + int ctr = 5000000; /* only try for about a tenth of a second */ + while( --ctr && !(DC390_read8 (DMA_Status) & DMA_XFER_DONE) && pSRB->SGToBeXferLen ) + DC390_write8 (DMA_Status, DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ + if (!ctr) printk (KERN_CRIT "DC390: DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr)); + DC390_write8 (DMA_Status, DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; if( pSRB->SGIndex < pSRB->SGcount ) @@ -303,11 +402,7 @@ pSRB->pSegmentList++; psgl = pSRB->pSegmentList; -#ifndef VERSION_ELF_1_2_13 - pSRB->SGPhysAddr = virt_to_phys( psgl->address ); -#else - pSRB->SGPhysAddr = (ULONG) psgl->address; -#endif + pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; } else @@ -315,51 +410,50 @@ } else { - bval = inb( ioport+Current_Fifo ); - bval &= 0x1f; - ResidCnt = (ULONG) inb(ioport+CtcReg_High); - ResidCnt = ResidCnt << 8; - ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid); - ResidCnt = ResidCnt << 8; - ResidCnt |= (ULONG) inb(ioport+CtcReg_Low); - ResidCnt += (ULONG) bval; + ResidCnt = (ULONG) DC390_read8 (Current_Fifo) & 0x1f; + ResidCnt |= (ULONG) DC390_read8 (CtcReg_High) << 16; + ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid) << 8; + ResidCnt += (ULONG) DC390_read8 (CtcReg_Low); xferCnt = pSRB->SGToBeXferLen - ResidCnt; - pSRB->SGPhysAddr += xferCnt; + pSRB->SGBusAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; } } - bval = WRITE_DIRECTION+DMA_IDLE_CMD; - outb( bval, ioport+DMA_Cmd); + DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ } -static void -DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +void +dc390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR sstatus, bval; - USHORT i, ioport, residual; + UCHAR sstatus, residual, bval; PSGL psgl; - ULONG ResidCnt, xferCnt; + ULONG ResidCnt, xferCnt, i; PUCHAR ptr; - - ioport = pACB->IOPortBase; sstatus = *psstatus; if( !(pSRB->SRBState & SRB_XFERPAD) ) { - if( sstatus & PARITY_ERR ) + if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR)) pSRB->SRBStatus |= PARITY_ERROR; if( sstatus & COUNT_2_ZERO ) { - bval = inb(ioport+DMA_Status); - while( !(bval & DMA_XFER_DONE) ) - bval = inb(ioport+DMA_Status); + int ctr = 5000000; /* only try for about a tenth of a second */ + int dstate = 0; + while( --ctr && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE) && pSRB->SGToBeXferLen ) + DC390_write8 (DMA_Status, DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ + if (!ctr) printk (KERN_CRIT "DC390: DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n", DC390_read32 (DMA_Wk_ByteCntr)); + if (!ctr) printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate); + DEBUG1(ResidCnt = ((ULONG) DC390_read8 (CtcReg_High) << 16) \ + + ((ULONG) DC390_read8 (CtcReg_Mid) << 8) \ + + ((ULONG) DC390_read8 (CtcReg_Low));) + DEBUG1(printk ("Count_2_Zero (ResidCnt=%li,ToBeXfer=%li),", ResidCnt, pSRB->SGToBeXferLen);) + DC390_write8 (DMA_Status, DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ - bval = READ_DIRECTION+DMA_IDLE_CMD; - outb( bval, ioport+DMA_Cmd); + DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ pSRB->TotalXferredLen += pSRB->SGToBeXferLen; pSRB->SGIndex++; @@ -368,11 +462,7 @@ pSRB->pSegmentList++; psgl = pSRB->pSegmentList; -#ifndef VERSION_ELF_1_2_13 - pSRB->SGPhysAddr = virt_to_phys( psgl->address ); -#else - pSRB->SGPhysAddr = (ULONG) psgl->address; -#endif + pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; } else @@ -381,14 +471,15 @@ else /* phase changed */ { residual = 0; - bval = inb(ioport+Current_Fifo); + bval = DC390_read8 (Current_Fifo); while( bval & 0x1f ) { + DEBUG1(printk ("Check for residuals,");) if( (bval & 0x1f) == 1 ) { - for(i=0; i< 0x100; i++) + for(i=0; i < 0x100; i++) { - bval = inb(ioport+Current_Fifo); + bval = DC390_read8 (Current_Fifo); if( !(bval & 0x1f) ) goto din_1; else if( i == 0x0ff ) @@ -399,91 +490,85 @@ } } else - bval = inb(ioport+Current_Fifo); + bval = DC390_read8 (Current_Fifo); } din_1: - bval = READ_DIRECTION+DMA_BLAST_CMD; - outb(bval, ioport+DMA_Cmd); - for(i=0; i<0x8000; i++) + DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD); + for (i=0; i<0x8000; i++) { - bval = inb(ioport+DMA_Status); - if(bval & BLAST_COMPLETE) + bval = DC390_read8 (DMA_Status); + DC390_write8 (DMA_Status, BLAST_COMPLETE | DMA_XFER_DONE | DMA_XFER_ABORT | DMA_XFER_ERROR | PCI_MS_ABORT); /* clear */ + if (bval & BLAST_COMPLETE) break; } - bval = READ_DIRECTION+DMA_IDLE_CMD; - outb(bval, ioport+DMA_Cmd); + if (i == 0x8000) printk (KERN_CRIT "DC390: DMA Blast aborted unfinished!!\n"); + //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD); /* | DMA_INT */ - ResidCnt = (ULONG) inb(ioport+CtcReg_High); - ResidCnt = ResidCnt << 8; - ResidCnt |= (ULONG) inb(ioport+CtcReg_Mid); - ResidCnt = ResidCnt << 8; - ResidCnt |= (ULONG) inb(ioport+CtcReg_Low); + DEBUG1(printk ("Blast: Read %i times DMA_Status %02x", i, bval);) + ResidCnt = (ULONG) DC390_read8 (CtcReg_High); + ResidCnt <<= 8; + ResidCnt |= (ULONG) DC390_read8 (CtcReg_Mid); + ResidCnt <<= 8; + ResidCnt |= (ULONG) DC390_read8 (CtcReg_Low); xferCnt = pSRB->SGToBeXferLen - ResidCnt; - pSRB->SGPhysAddr += xferCnt; + pSRB->SGBusAddr += xferCnt; pSRB->TotalXferredLen += xferCnt; pSRB->SGToBeXferLen = ResidCnt; if( residual ) { - bval = inb(ioport+ScsiFifo); /* get residual byte */ -#ifndef VERSION_ELF_1_2_13 - ptr = (PUCHAR) phys_to_virt( pSRB->SGPhysAddr ); -#else - ptr = (PUCHAR) pSRB->SGPhysAddr; -#endif + bval = DC390_read8 (ScsiFifo); /* get one residual byte */ + ptr = (PUCHAR) bus_to_virt( pSRB->SGBusAddr ); *ptr = bval; - pSRB->SGPhysAddr++; + pSRB->SGBusAddr++; xferCnt++; pSRB->TotalXferredLen++; pSRB->SGToBeXferLen--; } + DEBUG1(printk ("Xfered: %li, Total: %li, Remaining: %li\n", xferCnt,\ + pSRB->TotalXferredLen, pSRB->SGToBeXferLen);) + } } } static void -DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { } static void -DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR bval; - USHORT ioport; - ioport = pACB->IOPortBase; - bval = inb(ioport+ScsiFifo); - pSRB->TargetStatus = bval; - bval++; - bval = inb(ioport+ScsiFifo); /* get message */ - pSRB->EndMessage = bval; + pSRB->TargetStatus = DC390_read8 (ScsiFifo); + //udelay (1); + pSRB->EndMessage = DC390_read8 (ScsiFifo); /* get message */ *psstatus = SCSI_NOP0; pSRB->SRBState = SRB_COMPLETED; - bval = MSG_ACCEPTED_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); } static void -DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) ) *psstatus = SCSI_NOP0; + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } -static void -DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +void +dc390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { UCHAR bval; - USHORT ioport, wval, wval1; + USHORT wval, wval1; PDCB pDCB; PSRB psrb; - ioport = pACB->IOPortBase; pDCB = pACB->pActiveDCB; - bval = inb( ioport+ScsiFifo ); + bval = DC390_read8 (ScsiFifo); if( !(pSRB->SRBState & SRB_MSGIN_MULTI) ) { if(bval == MSG_DISCONNECT) @@ -498,12 +583,12 @@ pSRB->SRBState |= SRB_MSGIN_MULTI; pSRB->MsgInBuf[0] = bval; pSRB->MsgCnt = 1; - pSRB->pMsgPtr = &pSRB->MsgInBuf[1]; + pSRB->pMsgPtr = &(pSRB->MsgInBuf[1]); } else if(bval == MSG_REJECT_) { - bval = RESET_ATN_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, RESET_ATN_CMD); + pDCB->NegoPeriod = 50; if( pSRB->SRBState & DO_SYNC_NEGO) goto set_async; } @@ -529,7 +614,7 @@ psrb = pDCB->pGoingLast; if( pSRB ) { - for( ;; ) + for( ;pSRB ; ) { if(pSRB->TagNumber != bval) { @@ -543,7 +628,7 @@ if( pDCB->DCBFlag & ABORT_DEV_ ) { pSRB->SRBState = SRB_ABORT_SENT; - EnableMsgOut( pACB, pSRB ); + dc390_EnableMsgOut( pACB, pSRB ); } if( !(pSRB->SRBState & SRB_DISCONNECT) ) goto mingx0; @@ -557,238 +642,199 @@ pSRB->SRBState = SRB_UNEXPECT_RESEL; pDCB->pActiveSRB = pSRB; pSRB->MsgOutBuf[0] = MSG_ABORT_TAG; - EnableMsgOut2( pACB, pSRB ); + dc390_EnableMsgOut2( pACB, pSRB ); } } } else if( (pSRB->MsgInBuf[0] == MSG_EXTENDED) && (pSRB->MsgCnt == 5) ) - { - pSRB->SRBState &= ~(SRB_MSGIN_MULTI+DO_SYNC_NEGO); - if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != 1) ) + { /* Note: This will fail for target initiated SDTR ? */ + pSRB->SRBState &= ~(SRB_MSGIN_MULTI); + if( (pSRB->MsgInBuf[1] != 3) || (pSRB->MsgInBuf[2] != EXTENDED_SDTR) ) { /* reject_msg: */ pSRB->MsgCnt = 1; pSRB->MsgInBuf[0] = MSG_REJECT_; - bval = SET_ATN_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, SET_ATN_CMD); } else if( !(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4]) ) { set_async: pDCB = pSRB->pSRBDCB; + if (!(pSRB->SRBState & DO_SYNC_NEGO)) + printk ("DC390: Target (%i,%i) initiates Non-Sync?\n", pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + pSRB->SRBState &= ~DO_SYNC_NEGO; pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; - pDCB->CtrlR3 = FAST_CLK; /* ;non_fast */ + pDCB->CtrlR3 = FAST_CLK; /* fast clock / normal scsi */ pDCB->CtrlR4 &= 0x3f; - pDCB->CtrlR4 |= EATER_25NS; /* ; 25ns glitch eater */ - goto re_prog; + pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ + goto re_prog; } else { /* set_sync: */ pDCB = pSRB->pSRBDCB; + if (!(pSRB->SRBState & DO_SYNC_NEGO)) + printk ("DC390: Target (%i,%i) initiates Sync: %ins %i ?\n", + pDCB->UnitSCSIID, pDCB->UnitSCSILUN, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]); + pSRB->SRBState &= ~DO_SYNC_NEGO; pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE; pDCB->SyncOffset &= 0x0f0; pDCB->SyncOffset |= pSRB->MsgInBuf[4]; pDCB->NegoPeriod = pSRB->MsgInBuf[3]; wval = (USHORT) pSRB->MsgInBuf[3]; - wval = wval << 2; - wval--; - wval1 = wval / 25; + wval = wval << 2; wval -= 3; wval1 = wval / 25; /* compute speed */ if( (wval1 * 25) != wval) wval1++; - bval = FAST_CLK+FAST_SCSI; - pDCB->CtrlR4 &= 0x3f; - if(wval1 >= 8) + bval = FAST_CLK+FAST_SCSI; /* fast clock / fast scsi */ + pDCB->CtrlR4 &= 0x3f; /* Glitch eater: 12ns less than normal */ + if (pACB->glitch_cfg != NS_TO_GLITCH(0)) + pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1)); + else + pDCB->CtrlR4 |= NS_TO_GLITCH(0); + if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */ + if (wval1 >= 8) { - wval1--; - bval = FAST_CLK; /* ;fast clock/normal scsi */ - pDCB->CtrlR4 |= EATER_25NS; /* ;25 ns glitch eater */ + wval1--; /* Timing computation differs by 1 from FAST_SCSI */ + bval = FAST_CLK; /* fast clock / normal scsi */ + pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */ } pDCB->CtrlR3 = bval; pDCB->SyncPeriod = (UCHAR)wval1; re_prog: - bval = pDCB->SyncPeriod; - outb(bval, ioport+Sync_Period); - bval = pDCB->SyncOffset; - outb(bval, ioport+Sync_Offset); - bval = pDCB->CtrlR3; - outb(bval, ioport+CtrlReg3); - bval = pDCB->CtrlR4; - outb(bval, ioport+CtrlReg4); - SetXferRate( pACB, pDCB); + DC390_write8 (Sync_Period, pDCB->SyncPeriod); + DC390_write8 (Sync_Offset, pDCB->SyncOffset); + DC390_write8 (CtrlReg3, pDCB->CtrlR3); + DC390_write8 (CtrlReg4, pDCB->CtrlR4); + dc390_SetXferRate (pACB, pDCB); } } } min6: *psstatus = SCSI_NOP0; - bval = MSG_ACCEPTED_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } -static void -DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir) +void +dc390_DataIO_Comm( PACB pACB, PSRB pSRB, UCHAR ioDir) { PSGL psgl; - UCHAR bval; - USHORT ioport; ULONG lval; - - ioport = pACB->IOPortBase; if( pSRB->SGIndex < pSRB->SGcount ) { - bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */ - outb( bval, ioport+DMA_Cmd); + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir /* | DMA_INT */); if( !pSRB->SGToBeXferLen ) { psgl = pSRB->pSegmentList; -#ifndef VERSION_ELF_1_2_13 - pSRB->SGPhysAddr = virt_to_phys( psgl->address ); -#else - pSRB->SGPhysAddr = (ULONG) psgl->address; -#endif + pSRB->SGBusAddr = virt_to_bus( psgl->address ); pSRB->SGToBeXferLen = (ULONG) psgl->length; + DEBUG1(printk (" DC390: Next SG segment.");) } lval = pSRB->SGToBeXferLen; - bval = (UCHAR) lval; - outb(bval,ioport+CtcReg_Low); - lval = lval >> 8; - bval = (UCHAR) lval; - outb(bval,ioport+CtcReg_Mid); - lval = lval >> 8; - bval = (UCHAR) lval; - outb(bval,ioport+CtcReg_High); - - lval = pSRB->SGToBeXferLen; - outl(lval, ioport+DMA_XferCnt); + DEBUG1(printk (" DC390: Transfer %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr);) + DC390_write8 (CtcReg_Low, (UCHAR) lval); + lval >>= 8; + DC390_write8 (CtcReg_Mid, (UCHAR) lval); + lval >>= 8; + DC390_write8 (CtcReg_High, (UCHAR) lval); - lval = pSRB->SGPhysAddr; - outl( lval, ioport+DMA_XferAddr); - - bval = DMA_COMMAND+INFO_XFER_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen); + DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr); + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); /* | DMA_INT; */ pSRB->SRBState = SRB_DATA_XFER; - bval = DMA_IDLE_CMD | ioDir; /* ;+EN_DMA_INT */ - outb(bval, ioport+DMA_Cmd); + DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD); - bval = DMA_START_CMD | ioDir; /* ;+EN_DMA_INT */ - outb(bval, ioport+DMA_Cmd); + DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT); + //DEBUG1(printk ("DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status));) } else /* xfer pad */ { + UCHAR bval = 0; if( pSRB->SGcount ) { pSRB->AdaptStatus = H_OVER_UNDER_RUN; pSRB->SRBStatus |= OVER_RUN; + DEBUG0(printk (" DC390: Overrun -");) } - bval = 0; - outb(bval,ioport+CtcReg_Low); - outb(bval,ioport+CtcReg_Mid); - outb(bval,ioport+CtcReg_High); + DEBUG0(printk (" Clear transfer pad \n");) + DC390_write8 (CtcReg_Low, bval); + DC390_write8 (CtcReg_Mid, bval); + DC390_write8 (CtcReg_High, bval); pSRB->SRBState |= SRB_XFERPAD; - bval = DMA_COMMAND+XFER_PAD_BYTE; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE); /* - bval = DMA_IDLE_CMD | ioDir; ;+EN_DMA_INT - outb(bval, ioport+DMA_Cmd); - bval = DMA_START_CMD | ioDir; ;+EN_DMA_INT - outb(bval, ioport+DMA_Cmd); + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir); // | DMA_INT; + DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir | DMA_INT); */ } } static void -DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR ioDir; - - ioDir = WRITE_DIRECTION; - DataIO_Comm( pACB, pSRB, ioDir); + dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION); } static void -DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR ioDir; - - ioDir = READ_DIRECTION; - DataIO_Comm( pACB, pSRB, ioDir); + dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION); } -static void -DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +void +dc390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { PDCB pDCB; - UCHAR bval; + UCHAR i, cnt; PUCHAR ptr; - USHORT ioport, i, cnt; - - ioport = pACB->IOPortBase; - bval = RESET_ATN_CMD; - outb(bval, ioport+ScsiCmd); - bval = CLEAR_FIFO_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, RESET_ATN_CMD); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( !(pSRB->SRBFlag & AUTO_REQSENSE) ) { - cnt = (USHORT) pSRB->ScsiCmdLen; + cnt = (UCHAR) pSRB->ScsiCmdLen; ptr = (PUCHAR) pSRB->CmdBlock; for(i=0; i < cnt; i++) - { - outb(*ptr, ioport+ScsiFifo); - ptr++; - } + DC390_write8 (ScsiFifo, *(ptr++)); } else { - bval = REQUEST_SENSE; - outb(bval, ioport+ScsiFifo); + UCHAR bval = 0; + DC390_write8 (ScsiFifo, REQUEST_SENSE); pDCB = pACB->pActiveDCB; - bval = pDCB->IdentifyMsg << 5; - outb(bval, ioport+ScsiFifo); - bval = 0; - outb(bval, ioport+ScsiFifo); - outb(bval, ioport+ScsiFifo); - bval = sizeof(pSRB->pcmd->sense_buffer); - outb(bval, ioport+ScsiFifo); - bval = 0; - outb(bval, ioport+ScsiFifo); + DC390_write8 (ScsiFifo, pDCB->IdentifyMsg << 5); + DC390_write8 (ScsiFifo, bval); + DC390_write8 (ScsiFifo, bval); + DC390_write8 (ScsiFifo, sizeof(pSRB->pcmd->sense_buffer)); + DC390_write8 (ScsiFifo, bval); } pSRB->SRBState = SRB_COMMAND; - bval = INFO_XFER_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, INFO_XFER_CMD); } static void -DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR bval; - USHORT ioport; - - ioport = pACB->IOPortBase; - bval = CLEAR_FIFO_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); pSRB->SRBState = SRB_STATUS; - bval = INITIATOR_CMD_CMPLTE; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE); + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } -static void -DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +void +dc390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR bval; - USHORT ioport, i, cnt; + UCHAR bval, i, cnt; PUCHAR ptr; PDCB pDCB; - ioport = pACB->IOPortBase; - bval = CLEAR_FIFO_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); pDCB = pACB->pActiveDCB; if( !(pSRB->SRBState & SRB_MSGOUT) ) { @@ -797,10 +843,7 @@ { ptr = (PUCHAR) pSRB->MsgOutBuf; for(i=0; i < cnt; i++) - { - outb(*ptr, ioport+ScsiFifo); - ptr++; - } + DC390_write8 (ScsiFifo, *(ptr++)); pSRB->MsgCnt = 0; if( (pDCB->DCBFlag & ABORT_DEV_) && (pSRB->MsgOutBuf[0] == MSG_ABORT) ) @@ -816,76 +859,67 @@ if( pDCB->SyncMode & SYNC_ENABLE ) goto mop1; } - outb(bval, ioport+ScsiFifo); + DC390_write8 (ScsiFifo, bval); } - bval = INFO_XFER_CMD; - outb( bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, INFO_XFER_CMD); } else { mop1: - bval = MSG_EXTENDED; - outb(bval, ioport+ScsiFifo); - bval = 3; /* ;length of extended msg */ - outb(bval, ioport+ScsiFifo); - bval = 1; /* ; sync nego */ - outb(bval, ioport+ScsiFifo); - bval = pDCB->NegoPeriod; - outb(bval, ioport+ScsiFifo); - bval = SYNC_NEGO_OFFSET; - outb(bval, ioport+ScsiFifo); + //printk ("DC390: Send SDTR message to %i %i ... \n", pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + DC390_write8 (ScsiFifo, MSG_EXTENDED); + DC390_write8 (ScsiFifo, 3); /* ;length of extended msg */ + DC390_write8 (ScsiFifo, EXTENDED_SDTR); /* ; sync nego */ + DC390_write8 (ScsiFifo, pDCB->NegoPeriod); + if (pDCB->SyncOffset & 0x0f) + DC390_write8 (ScsiFifo, pDCB->SyncOffset); + else + DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET); pSRB->SRBState |= DO_SYNC_NEGO; - bval = INFO_XFER_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, INFO_XFER_CMD); } } static void -DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus) { - UCHAR bval; - USHORT ioport; - - ioport = pACB->IOPortBase; - bval = CLEAR_FIFO_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( !(pSRB->SRBState & SRB_MSGIN) ) { - pSRB->SRBState &= SRB_DISCONNECT; + pSRB->SRBState &= ~SRB_DISCONNECT; pSRB->SRBState |= SRB_MSGIN; } - bval = INFO_XFER_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, INFO_XFER_CMD); + //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); } static void -DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus) { } static void -DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus) +dc390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus) { } static void -SetXferRate( PACB pACB, PDCB pDCB ) +dc390_SetXferRate( PACB pACB, PDCB pDCB ) { - UCHAR bval; - USHORT cnt, i; + UCHAR bval, i, cnt; PDCB ptr; if( !(pDCB->IdentifyMsg & 0x07) ) { if( pACB->scan_devices ) { - CurrSyncOffset = pDCB->SyncOffset; + dc390_CurrSyncOffset = pDCB->SyncOffset; } else { ptr = pACB->pLinkDCB; - cnt = pACB->DeviceCnt; + cnt = pACB->DCBCnt; bval = pDCB->UnitSCSIID; for(i=0; iIOPortBase; pDCB = pACB->pActiveDCB; if (!pDCB) { -#ifdef DC390_DEBUG0 - printk("ACB:%08lx->ActiveDCB:%08lx !,",(ULONG)pACB,(ULONG)pDCB); -#endif - restore_flags(flags); return; + int j = 400; + DEBUG0(printk(KERN_WARNING "ACB:%08lx->ActiveDCB:%08lx IOPort:%04x IRQ:%02x !\n",\ + (ULONG)pACB,(ULONG)pDCB,pACB->IOPortBase,pACB->IRQLevel);) + while (--j) udelay (1000); + DC390_read8 (INT_Status); /* Reset Pending INT */ + DC390_write8 (ScsiCmd, EN_SEL_RESEL); + return; } pSRB = pDCB->pActiveSRB; pACB->pActiveDCB = 0; pSRB->ScsiPhase = SCSI_NOP0; - bval = EN_SEL_RESEL; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, EN_SEL_RESEL); if( pSRB->SRBState & SRB_UNEXPECT_RESEL ) { pSRB->SRBState = 0; - DoWaitingSRB( pACB ); + dc390_DoWaitingSRB( pACB ); } else if( pSRB->SRBState & SRB_ABORT_SENT ) { @@ -954,7 +983,7 @@ pSRB = psrb; } pDCB->pGoingSRB = 0; - DoWaitingSRB( pACB ); + dc390_DoWaitingSRB( pACB ); } else { @@ -964,7 +993,7 @@ if( !(pACB->scan_devices) ) { pSRB->SRBState = SRB_READY; - RewaitSRB( pDCB, pSRB); + dc390_RewaitSRB( pDCB, pSRB); } else { @@ -974,76 +1003,66 @@ } else if( pSRB->SRBState & SRB_DISCONNECT ) { - DoWaitingSRB( pACB ); + dc390_DoWaitingSRB( pACB ); } else if( pSRB->SRBState & SRB_COMPLETED ) { disc1: if(pDCB->MaxCommand > 1) { - bval = pSRB->TagNumber; - pDCB->TagMask &= (~(1 << bval)); /* free tag mask */ + pDCB->TagMask &= (~(1 << pSRB->TagNumber)); /* free tag mask */ } pDCB->pActiveSRB = 0; pSRB->SRBState = SRB_FREE; - SRBdone( pACB, pDCB, pSRB); + dc390_SRBdone( pACB, pDCB, pSRB); } } - restore_flags(flags); return; } -static void -DC390_Reselect( PACB pACB ) +void +dc390_Reselect( PACB pACB ) { - PDCB pDCB, pdcb; + PDCB pDCB; PSRB pSRB; - USHORT ioport, wval; - UCHAR bval, bval1; - + USHORT wval; + UCHAR bval; -#ifdef DC390_DEBUG0 - printk("RSEL,"); -#endif - ioport = pACB->IOPortBase; + DEBUG0(printk(KERN_INFO "RSEL,");) pDCB = pACB->pActiveDCB; if( pDCB ) { /* Arbitration lost but Reselection win */ + DEBUG0(printk ("(ActiveDCB != 0)");) pSRB = pDCB->pActiveSRB; if( !( pACB->scan_devices ) ) { pSRB->SRBState = SRB_READY; - RewaitSRB( pDCB, pSRB); + dc390_RewaitSRB( pDCB, pSRB); } } - bval = inb(ioport+ScsiFifo); /* get ID */ - bval = bval ^ pACB->HostID_Bit; + bval = DC390_read8 (ScsiFifo); /* get ID */ + DEBUG0(printk ("Dev %02x,", bval);) + bval ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */ wval = 0; - bval1 = 1; - for(;;) - { - if( !(bval & bval1) ) - { - bval1 = bval1 << 1; - wval++; - } - else - break; - } - wval |= ( (USHORT) inb(ioport+ScsiFifo) & 7) << 8; /* get LUN */ + while (bval >>= 1) wval++; + wval |= ( (USHORT) DC390_read8 (ScsiFifo) & 7) << 8; /* get LUN */ + DEBUG0(printk ("(ID %02x, LUN %02x),", wval & 0xff, (wval & 0xff00) >> 8);) pDCB = pACB->pLinkDCB; - pdcb = pDCB; while( wval != *((PUSHORT) &pDCB->UnitSCSIID) ) { pDCB = pDCB->pNextDCB; - if( pDCB == pdcb ) + if( pDCB == pACB->pLinkDCB ) + { + printk (KERN_ERR "DC390: Reselect from non existing device (ID %02x, LUN %02x)\n", + wval & 0xff, (wval & 0xff00) >> 8); return; + } } pACB->pActiveDCB = pDCB; - if( pDCB->SyncMode & EN_TAG_QUEUING ) + if( pDCB->SyncMode & EN_TAG_QUEUEING ) { - pSRB = pACB->pTmpSRB; + pSRB = pACB->pTmpSRB; /* ?? */ pDCB->pActiveSRB = pSRB; } else @@ -1053,57 +1072,163 @@ { pSRB= pACB->pTmpSRB; pSRB->SRBState = SRB_UNEXPECT_RESEL; + printk (KERN_ERR "DC390: Reselect without outstanding cmnd (ID %02x, LUN %02x)\n", + wval & 0xff, (wval & 0xff00) >> 8); pDCB->pActiveSRB = pSRB; - EnableMsgOut( pACB, pSRB ); + dc390_EnableMsgOut( pACB, pSRB ); } else { if( pDCB->DCBFlag & ABORT_DEV_ ) { pSRB->SRBState = SRB_ABORT_SENT; - EnableMsgOut( pACB, pSRB ); + printk (KERN_NOTICE "DC390: Reselect: Abort (ID %02x, LUN %02x)\n", + wval & 0xff, (wval & 0xff00) >> 8); + dc390_EnableMsgOut( pACB, pSRB ); } else pSRB->SRBState = SRB_DATA_XFER; } } + + DEBUG1(printk ("Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber);) pSRB->ScsiPhase = SCSI_NOP0; - bval = pDCB->UnitSCSIID; - outb( bval, ioport+Scsi_Dest_ID); - bval = pDCB->SyncPeriod; - outb(bval, ioport+Sync_Period); - bval = pDCB->SyncOffset; - outb( bval, ioport+Sync_Offset); - bval = pDCB->CtrlR1; - outb(bval, ioport+CtrlReg1); - bval = pDCB->CtrlR3; - outb(bval, ioport+CtrlReg3); - bval = pDCB->CtrlR4; /* ; Glitch eater */ - outb(bval, ioport+CtrlReg4); - bval = MSG_ACCEPTED_CMD; /* ;to rls the /ACK signal */ - outb(bval, ioport+ScsiCmd); + DC390_write8 (Scsi_Dest_ID, pDCB->UnitSCSIID); + DC390_write8 (Sync_Period, pDCB->SyncPeriod); + DC390_write8 (Sync_Offset, pDCB->SyncOffset); + DC390_write8 (CtrlReg1, pDCB->CtrlR1); + DC390_write8 (CtrlReg3, pDCB->CtrlR3); + DC390_write8 (CtrlReg4, pDCB->CtrlR4); /* ; Glitch eater */ + DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); /* ;to release the /ACK signal */ } -static void -SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) +static void +dc390_remove_dev (PACB pACB, PDCB pDCB) +{ + PDCB pPrevDCB = pACB->pLinkDCB; + + pACB->DCBmap[pDCB->UnitSCSIID] &= ~(1 << pDCB->UnitSCSILUN); + if (pDCB->GoingSRBCnt > 1) + { + DCBDEBUG(printk (KERN_INFO "DC390: Driver won't free DCB (ID %i, LUN %i): 0x%08x because of SRBCnt %i\n",\ + pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB, pDCB->GoingSRBCnt);) + return; + }; + + if (pDCB == pACB->pLinkDCB) + { + if (pDCB->pNextDCB == pDCB) pDCB->pNextDCB = 0; + pACB->pLinkDCB = pDCB->pNextDCB; + pACB->pLastDCB->pNextDCB = pDCB->pNextDCB; + } + else + { + while (pPrevDCB->pNextDCB != pDCB) pPrevDCB = pPrevDCB->pNextDCB; + pPrevDCB->pNextDCB = pDCB->pNextDCB; + if (pDCB == pACB->pLastDCB) pACB->pLastDCB = pPrevDCB; + } + + DCBDEBUG(printk (KERN_INFO "DC390: Driver about to free DCB (ID %i, LUN %i): 0x%08x\n",\ + pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);) + kfree (pDCB); if (pDCB == pACB->pActiveDCB) pACB->pActiveDCB = 0; + pACB->DCBCnt--; + /* pACB->DeviceCnt--; */ +}; + + +static UCHAR __inline__ +dc390_tagq_blacklist (char* name) +{ + UCHAR i; + for(i=0; iVers & 0x07) >= 2 || (ptr->RDF & 0x0F) == 2 ) + { + if ( (ptr->Flags & SCSI_INQ_CMDQUEUE) && + (pDCB->DevMode & TAG_QUEUEING_) && + /* ((pDCB->DevType == TYPE_DISK) + || (pDCB->DevType == TYPE_MOD)) &&*/ + !dc390_tagq_blacklist (((char*)ptr)+8) ) + { + pDCB->MaxCommand = pDCB->pDCBACB->TagMaxNum; + pDCB->SyncMode |= EN_TAG_QUEUEING /* | EN_ATN_STOP */; + pDCB->TagMask = 0; + } + else + { + /* Do we really need to check for DevType here ? */ + if ( 0 /*(pDCB->DevMode & EN_DISCONNECT_)*/ + /* && ((pDCB->DevType == TYPE_DISK) + || (pDCB->DevType == TYPE_MOD))*/ ) + pDCB->SyncMode |= EN_ATN_STOP; + else + //pDCB->SyncMode &= ~EN_ATN_STOP; + pDCB->SyncMode &= ~0; + } + } +}; + + +static void +dc390_add_dev (PACB pACB, PDCB pDCB, PSCSI_INQDATA ptr) +{ + UCHAR bval1 = ptr->DevType & SCSI_DEVTYPE; + pDCB->DevType = bval1; + /* if (bval1 == TYPE_DISK || bval1 == TYPE_MOD) */ + dc390_disc_tagq_set (pDCB, ptr); +}; + + +void +dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSRB psrb; - UCHAR bval, bval1, i, j, status; + UCHAR bval, status, i; PSCSICMD pcmd; PSCSI_INQDATA ptr; - USHORT disable_tag; - ULONG flags; PSGL ptr2; ULONG swlval; pcmd = pSRB->pcmd; status = pSRB->TargetStatus; + DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p, pid %li\n", status, pcmd->result,\ + pSRB, pcmd->pid);) if(pSRB->SRBFlag & AUTO_REQSENSE) - { + { /* Last command was a Request Sense */ pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = SCSI_STAT_CHECKCOND; +#ifdef DC390_REMOVABLEDEBUG + switch (pcmd->sense_buffer[2] & 0x0f) + { + case NOT_READY: printk (KERN_INFO "DC390: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, + status, pACB->scan_devices); break; + case UNIT_ATTENTION: printk (KERN_INFO "DC390: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, + status, pACB->scan_devices); break; + case ILLEGAL_REQUEST: printk (KERN_INFO "DC390: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, + status, pACB->scan_devices); break; + case MEDIUM_ERROR: printk (KERN_INFO "DC390: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, + status, pACB->scan_devices); break; + case HARDWARE_ERROR: printk (KERN_INFO "DC390: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN, + status, pACB->scan_devices); break; + } +#endif + //pcmd->result = DRIVER_SENSE << 24 | DID_OK << 16 | status; if(status == SCSI_STAT_CHECKCOND) { pcmd->result = DID_BAD_TARGET << 16; @@ -1111,7 +1236,7 @@ } if(pSRB->RetryCnt == 0) { - *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; + (ULONG)(pSRB->CmdBlock[0]) = pSRB->Segment0[0]; pSRB->TotalXferredLen = pSRB->Segment1[1]; if( (pSRB->TotalXferredLen) && (pSRB->TotalXferredLen >= pcmd->underflow) ) @@ -1119,28 +1244,29 @@ else pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | SCSI_STAT_CHECKCOND; -#ifdef DC390_DEBUG0 - printk("Cmd=%2x,Result=%8x,XferL=%8x,",pSRB->CmdBlock[0], - (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen); -#endif + REMOVABLEDEBUG(printk("Cmd=%02x,Result=%08x,XferL=%08x\n",pSRB->CmdBlock[0],\ + (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);) goto ckc_e; } - else + else /* Retry */ { pSRB->RetryCnt--; pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; *((PULONG) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0]; *((PULONG) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1]; - if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) + /* Don't retry on TEST_UNIT_READY */ + if( pSRB->CmdBlock[0] == TEST_UNIT_READY /* || pSRB->CmdBlock[0] == START_STOP */) { - pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) | - SCSI_STAT_CHECKCOND; + pcmd->result = (DRIVER_SENSE << 24) | (DRIVER_OK << 16) + | SCSI_STAT_CHECKCOND; + REMOVABLEDEBUG(printk("Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->CmdBlock[0],\ + (UINT) pcmd->result, (UINT) pSRB->TotalXferredLen);) goto ckc_e; } pcmd->result |= (DRIVER_SENSE << 24); - pSRB->SGcount = (UCHAR) pSRB->Segment1[0]; - pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8); + pSRB->SGcount = (UCHAR) pSRB->Segment1[0]; + pSRB->ScsiCmdLen = (UCHAR) (pSRB->Segment1[0] >> 8); pSRB->SGIndex = 0; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; @@ -1152,8 +1278,8 @@ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; pSRB->Segmentx.length = pcmd->request_bufflen; } - if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) - RewaitSRB( pDCB, pSRB ); + if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) + dc390_RewaitSRB( pDCB, pSRB ); return; } } @@ -1161,6 +1287,8 @@ { if( status == SCSI_STAT_CHECKCOND) { + REMOVABLEDEBUG(printk (KERN_INFO "DC390: Scsi_Stat_CheckCond (Cmd %02x, Id %02x, LUN %02x)\n",\ + pcmd->cmnd[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);) if( (pSRB->SGIndex < pSRB->SGcount) && (pSRB->SGcount) && (pSRB->SGToBeXferLen) ) { bval = pSRB->SGcount; @@ -1171,12 +1299,10 @@ swlval += ptr2->length; ptr2++; } -#ifdef DC390_DEBUG0 - printk("XferredLen=%8x,NotXferLen=%8x,", - (UINT) pSRB->TotalXferredLen, (UINT) swlval); -#endif + REMOVABLEDEBUG(printk("XferredLen=%08x,NotXferLen=%08x\n",\ + (UINT) pSRB->TotalXferredLen, (UINT) swlval);) } - RequestSense( pACB, pDCB, pSRB ); + dc390_RequestSense( pACB, pDCB, pSRB ); return; } else if( status == SCSI_STAT_QUEUEFULL ) @@ -1184,7 +1310,7 @@ bval = (UCHAR) pDCB->GoingSRBCnt; bval--; pDCB->MaxCommand = bval; - RewaitSRB( pDCB, pSRB ); + dc390_RewaitSRB( pDCB, pSRB ); pSRB->AdaptStatus = 0; pSRB->TargetStatus = 0; return; @@ -1194,12 +1320,22 @@ pSRB->AdaptStatus = H_SEL_TIMEOUT; pSRB->TargetStatus = 0; pcmd->result = DID_BAD_TARGET << 16; + /* Devices are removed below ... */ } - else + else if (status == SCSI_STAT_BUSY && + (pSRB->CmdBlock[0] == TEST_UNIT_READY || pSRB->CmdBlock[0] == INQUIRY) && + pACB->scan_devices) { pSRB->AdaptStatus = 0; + pSRB->TargetStatus = status; + pcmd->result = (ULONG) (pSRB->EndMessage << 8) + /* | (ULONG) status */; + } + else + { /* Another error */ + pSRB->AdaptStatus = 0; if( pSRB->RetryCnt ) - { + { /* Retry */ pSRB->RetryCnt--; pSRB->TargetStatus = 0; pSRB->SGIndex = 0; @@ -1213,19 +1349,19 @@ pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; pSRB->Segmentx.length = pcmd->request_bufflen; } - if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) - RewaitSRB( pDCB, pSRB ); + if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) + dc390_RewaitSRB( pDCB, pSRB ); return; } else - { + { /* Report error */ pcmd->result |= (DID_ERROR << 16) | (ULONG) (pSRB->EndMessage << 8) | (ULONG) status; } } } else - { + { /* Target status == 0 */ status = pSRB->AdaptStatus; if(status & H_OVER_UNDER_RUN) { @@ -1249,95 +1385,57 @@ { if( pSRB->CmdBlock[0] == TEST_UNIT_READY ) { - if(pcmd->result != (DID_OK << 16)) - { - if( pcmd->result & SCSI_STAT_CHECKCOND ) - { - goto RTN_OK; - } - else - { - pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); - pPrevDCB->pNextDCB = pACB->pLinkDCB; - if( (pcmd->target == pACB->max_id) && - ((pcmd->lun == 0) || (pcmd->lun == pACB->max_lun)) ) - { - pACB->scan_devices = 0; - } - } - } - else - { -RTN_OK: - pPrevDCB->pNextDCB = pDCB; - pDCB->pNextDCB = pACB->pLinkDCB; - if( (pcmd->target == pACB->max_id) && (pcmd->lun == pACB->max_lun) ) - pACB->scan_devices = END_SCAN; - } - } - else if( pSRB->CmdBlock[0] == INQUIRY ) - { - if( (pcmd->target == pACB->max_id) && - (pcmd->lun == pACB->max_lun) ) - { - pACB->scan_devices = 0; - } - ptr = (PSCSI_INQDATA) (pcmd->request_buffer); - if( pcmd->use_sg ) - ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address); - bval1 = ptr->DevType & SCSI_DEVTYPE; - if(bval1 == SCSI_NODEV) - { - pACB->DCBmap[pcmd->target] &= ~(1 << pcmd->lun); - pPrevDCB->pNextDCB = pACB->pLinkDCB; +#ifdef DC390_DEBUG0 + printk (KERN_INFO "DC390: Test_Unit_Ready: result: %08x", pcmd->result); + if (pcmd->result & DRIVER_SENSE << 24) printk (" (sense: %02x %02x %02x %02x)\n", + pcmd->sense_buffer[0], pcmd->sense_buffer[1], + pcmd->sense_buffer[2], pcmd->sense_buffer[3]); + else printk ("\n"); +#endif + if((pcmd->result != (DID_OK << 16) && !(pcmd->result & SCSI_STAT_CHECKCOND) && !(pcmd->result & SCSI_STAT_BUSY)) || + ((pcmd->result & DRIVER_SENSE << 24) && (pcmd->sense_buffer[0] & 0x70) == 0x70 && + (pcmd->sense_buffer[2] & 0xf) == ILLEGAL_REQUEST) || pcmd->result & DID_ERROR << 16) + { + /* device not present: remove */ + dc390_remove_dev (pACB, pDCB); + + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + ((pcmd->lun == 0) || (pcmd->lun == pACB->pScsiHost->max_lun - 1)) ) + pACB->scan_devices = 0; } else { - pACB->DeviceCnt++; - pPrevDCB = pDCB; - pACB->pDCB_free = (PDCB) ((ULONG) (pACB->pDCB_free) + sizeof( DC390_DCB )); - pDCB->DevType = bval1; - if(bval1 == TYPE_DISK || bval1 == TYPE_MOD) - { - if( (((ptr->Vers & 0x07) >= 2) || ((ptr->RDF & 0x0F) == 2)) && - (ptr->Flags & SCSI_INQ_CMDQUEUE) && - (pDCB->DevMode & TAG_QUEUING_) && - (pDCB->DevMode & EN_DISCONNECT_) ) - { - disable_tag = 0; - for(i=0; iMaxCommand = pACB->TagMaxNum; - pDCB->SyncMode |= EN_TAG_QUEUING; - pDCB->TagMask = 0; - } - else - { - pDCB->SyncMode |= EN_ATN_STOP; - } - } - } + /* device present: add */ + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) + pACB->scan_devices = END_SCAN ; + /* pACB->DeviceCnt++; */ /* Dev is added on INQUIRY */ } } } - - save_flags( flags ); - cli(); -/* ReleaseSRB( pDCB, pSRB ); */ + + if( pSRB->CmdBlock[0] == INQUIRY && + (pcmd->result == DID_OK << 16 || pcmd->result & SCSI_STAT_CHECKCOND) ) + { + ptr = (PSCSI_INQDATA) (pcmd->request_buffer); + if( pcmd->use_sg ) + ptr = (PSCSI_INQDATA) (((PSGL) ptr)->address); + if ((ptr->DevType & SCSI_DEVTYPE) == TYPE_NODEV) + { + /* device not present: remove */ + dc390_remove_dev (pACB, pDCB); + } + else + { + /* device found: add */ + dc390_add_dev (pACB, pDCB, ptr); + if (pACB->scan_devices) pACB->DeviceCnt++; + } + if( (pcmd->target == pACB->pScsiHost->max_id - 1) && + (pcmd->lun == pACB->pScsiHost->max_lun - 1) ) + pACB->scan_devices = 0; + }; +/* dc390_ReleaseSRB( pDCB, pSRB ); */ if(pSRB == pDCB->pGoingSRB ) { @@ -1356,33 +1454,34 @@ pACB->pFreeSRB = pSRB; pDCB->GoingSRBCnt--; - DoWaitingSRB( pACB ); - restore_flags(flags); + dc390_DoWaitingSRB( pACB ); -/* Notify cmd done */ + DC390_UNLOCK_ACB_NI; pcmd->scsi_done( pcmd ); + DC390_LOCK_ACB_NI; if( pDCB->QIORBCnt ) - DoNextCmd( pACB, pDCB ); + dc390_DoNextCmd( pACB, pDCB ); return; } -static void -DoingSRB_Done( PACB pACB ) +/* Remove all SRBs and tell midlevel code DID_RESET */ +void +dc390_DoingSRB_Done( PACB pACB ) { - PDCB pDCB, pdcb; - PSRB psrb, psrb2; - USHORT cnt, i; + PDCB pDCB, pdcb; + PSRB psrb, psrb2; + UCHAR i; PSCSICMD pcmd; pDCB = pACB->pLinkDCB; pdcb = pDCB; + if (! pdcb) return; do { - cnt = pdcb->GoingSRBCnt; psrb = pdcb->pGoingSRB; - for( i=0; iGoingSRBCnt; i++) { psrb2 = psrb->pNextSRB; pcmd = psrb->pcmd; @@ -1393,62 +1492,46 @@ psrb->pNextSRB = pACB->pFreeSRB; pACB->pFreeSRB = psrb; + DC390_UNLOCK_ACB_NI; pcmd->scsi_done( pcmd ); + DC390_LOCK_ACB_NI; psrb = psrb2; } pdcb->GoingSRBCnt = 0;; pdcb->pGoingSRB = NULL; pdcb->TagMask = 0; pdcb = pdcb->pNextDCB; - } - while( pdcb != pDCB ); + } while( pdcb != pDCB ); } static void -DC390_ResetSCSIBus( PACB pACB ) +dc390_ResetSCSIBus( PACB pACB ) { - USHORT ioport; - UCHAR bval; - ULONG flags; - - save_flags(flags); - cli(); pACB->ACBFlag |= RESET_DEV; - ioport = pACB->IOPortBase; - bval = DMA_IDLE_CMD; - outb(bval,ioport+DMA_Cmd); + DC390_write8 (ScsiCmd, RST_DEVICE_CMD); + udelay (250); + DC390_write8 (ScsiCmd, NOP_CMD); + + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD); - bval = RST_SCSI_BUS_CMD; - outb(bval,ioport+ScsiCmd); - - restore_flags(flags); return; } - static void -DC390_ScsiRstDetect( PACB pACB ) +dc390_ScsiRstDetect( PACB pACB ) { - ULONG wlval, flags; - USHORT ioport; - UCHAR bval; + printk ("DC390: Rst_Detect: laststat = %08lx\n", dc390_laststatus); + //DEBUG0(printk(KERN_INFO "RST_DETECT,");) -#ifdef DC390_DEBUG0 - printk("RST_DETEC"); -#endif - save_flags(flags); - sti(); - wlval = jiffies + HZ; - while( jiffies < wlval ); /* delay 1 sec */ - - cli(); - ioport = pACB->IOPortBase; - bval = DMA_IDLE_CMD; - outb(bval,ioport+DMA_Cmd); - bval = CLEAR_FIFO_CMD; - outb(bval,ioport+ScsiCmd); + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + /* Unlock before ? */ + /* delay a second */ + { unsigned int msec = 1*1000; while (--msec) udelay(1000); } + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); if( pACB->ACBFlag & RESET_DEV ) pACB->ACBFlag |= RESET_DONE; @@ -1456,30 +1539,32 @@ { pACB->ACBFlag |= RESET_DETECT; - ResetDevParam( pACB ); -/* DoingSRB_Done( pACB ); ???? */ - RecoverSRB( pACB ); + dc390_ResetDevParam( pACB ); +/* dc390_DoingSRB_Done( pACB ); ???? */ + dc390_RecoverSRB( pACB ); pACB->pActiveDCB = NULL; pACB->ACBFlag = 0; - DoWaitingSRB( pACB ); + dc390_DoWaitingSRB( pACB ); } - restore_flags(flags); return; } -static void -RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) +static void __inline__ +dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ) { PSCSICMD pcmd; + REMOVABLEDEBUG(printk (KERN_INFO "DC390: RequestSense (Cmd %02x, Id %02x, LUN %02x)\n",\ + pSRB->CmdBlock[0], pDCB->UnitSCSIID, pDCB->UnitSCSILUN);) + pSRB->SRBFlag |= AUTO_REQSENSE; - pSRB->Segment0[0] = *((PULONG) &(pSRB->CmdBlock[0])); - pSRB->Segment0[1] = *((PULONG) &(pSRB->CmdBlock[4])); + pSRB->Segment0[0] = (ULONG) pSRB->CmdBlock[0]; + pSRB->Segment0[1] = (ULONG) pSRB->CmdBlock[4]; pSRB->Segment1[0] = (ULONG) ((pSRB->ScsiCmdLen << 8) + pSRB->SGcount); pSRB->Segment1[1] = pSRB->TotalXferredLen; pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; + pSRB->TargetStatus = 0; /* SCSI_STAT_CHECKCOND; */ pcmd = pSRB->pcmd; @@ -1489,52 +1574,40 @@ pSRB->SGcount = 1; pSRB->SGIndex = 0; - *((PULONG) &(pSRB->CmdBlock[0])) = 0x00000003; + pSRB->CmdBlock[0] = REQUEST_SENSE; pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5; - *((PUSHORT) &(pSRB->CmdBlock[4])) = sizeof(pcmd->sense_buffer); + (USHORT) pSRB->CmdBlock[2] = 0; + (USHORT) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer); pSRB->ScsiCmdLen = 6; pSRB->TotalXferredLen = 0; pSRB->SGToBeXferLen = 0; - if( DC390_StartSCSI( pACB, pDCB, pSRB ) ) - RewaitSRB( pDCB, pSRB ); + if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) + dc390_RewaitSRB( pDCB, pSRB ); } -static void -EnableMsgOut2( PACB pACB, PSRB pSRB ) +static void __inline__ +dc390_EnableMsgOut2( PACB pACB, PSRB pSRB ) { - USHORT ioport; - UCHAR bval; - - ioport = pACB->IOPortBase; pSRB->MsgCnt = 1; - bval = SET_ATN_CMD; - outb(bval, ioport+ScsiCmd); + DC390_write8 (ScsiCmd, SET_ATN_CMD); } -static void -EnableMsgOut( PACB pACB, PSRB pSRB ) +static void __inline__ +dc390_EnableMsgOut( PACB pACB, PSRB pSRB ) { pSRB->MsgOutBuf[0] = MSG_ABORT; - EnableMsgOut2( pACB, pSRB ); + dc390_EnableMsgOut2( pACB, pSRB ); + pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_; } -static void -DC390_InvalidCmd( PACB pACB ) +static void __inline__ +dc390_InvalidCmd( PACB pACB ) { - UCHAR bval; - USHORT ioport; - PSRB pSRB; - - pSRB = pACB->pActiveDCB->pActiveSRB; - if( pSRB->SRBState & (SRB_START_+SRB_MSGOUT) ) - { - ioport = pACB->IOPortBase; - bval = CLEAR_FIFO_CMD; - outb(bval,(ioport+ScsiCmd)); - } + if( pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_+SRB_MSGOUT) ) + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); } diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tmscsim.c linux/drivers/scsi/tmscsim.c --- linux.vanilla/drivers/scsi/tmscsim.c Sun Jun 21 18:41:18 1998 +++ linux/drivers/scsi/tmscsim.c Sun Dec 6 03:15:09 1998 @@ -5,36 +5,137 @@ * Bus Master Host Adapter * * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. * ***********************************************************************/ -/* Minor enhancements and bugfixes by * +/* (C) Copyright: put under GNU GPL in 10/96 * +*************************************************************************/ +/* $Id: tmscsim.c,v 2.5 1998/11/05 10:16:43 garloff Exp $ */ +/* Enhancements and bugfixes by * * Kurt Garloff * ***********************************************************************/ /* HISTORY: * * * * REV# DATE NAME DESCRIPTION * - * 1.00 04/24/96 CLH First release * - * 1.01 06/12/96 CLH Fixed bug of Media Change for Removable * + * 1.00 96/04/24 CLH First release * + * 1.01 96/06/12 CLH Fixed bug of Media Change for Removable * * Device, scan all LUN. Support Pre2.0.10 * - * 1.02 06/18/96 CLH Fixed bug of Command timeout ... * - * 1.03 09/25/96 KG Added tmscsim_proc_info() * - * 1.04 10/11/96 CLH Updating for support KV 2.0.x * - * 1.05 10/18/96 KG Fixed bug in DC390_abort(null ptr deref)* - * 1.06 10/25/96 KG Fixed module support * - * 1.07 11/09/96 KG Fixed tmscsim_proc_info() * - * 1.08 11/18/96 KG Fixed null ptr in DC390_Disconnect() * - * 1.09 11/30/96 KG Added register the allocated IO space * - * 1.10 12/05/96 CLH Modified tmscsim_proc_info(), and reset * + * 1.02 96/06/18 CLH Fixed bug of Command timeout ... * + * 1.03 96/09/25 KG Added tmscsim_proc_info() * + * 1.04 96/10/11 CLH Updating for support KV 2.0.x * + * 1.05 96/10/18 KG Fixed bug in DC390_abort(null ptr deref)* + * 1.06 96/10/25 KG Fixed module support * + * 1.07 96/11/09 KG Fixed tmscsim_proc_info() * + * 1.08 96/11/18 KG Fixed null ptr in DC390_Disconnect() * + * 1.09 96/11/30 KG Added register the allocated IO space * + * 1.10 96/12/05 CLH Modified tmscsim_proc_info(), and reset * * pending interrupt in DC390_detect() * - * 1.11 02/05/97 KG/CLH Fixeds problem with partitions greater * - * than 1GB * + * 1.11 97/02/05 KG/CLH Fixeds problem with partitions greater * + * than 1GB * + * 1.12 98/02/15 MJ Rewritten PCI probing * + * 1.13 98/04/08 KG Support for non DC390, __initfunc decls,* + * changed max devs from 10 to 16 * + * 1.14a 98/05/05 KG Dynamic DCB allocation, add-single-dev * + * for LUNs if LUN_SCAN (BIOS) not set * + * runtime config using /proc interface * + * 1.14b 98/05/06 KG eliminated cli (); sti (); spinlocks * + * 1.14c 98/05/07 KG 2.0.x compatibility * + * 1.20a 98/05/07 KG changed names of funcs to be consistent * + * DC390_ (entry points), dc390_ (internal)* + * reworked locking * + * 1.20b 98/05/12 KG bugs: version, kfree, _ctmp * + * debug output * + * 1.20c 98/05/12 KG bugs: kfree, parsing, EEpromDefaults * + * 1.20d 98/05/14 KG bugs: list linkage, clear flag after * + * reset on startup, code cleanup * + * 1.20e 98/05/15 KG spinlock comments, name space cleanup * + * pLastDCB now part of ACB structure * + * added stats, timeout for 2.1, TagQ bug * + * RESET and INQUIRY interface commands * + * 1.20f 98/05/18 KG spinlocks fixes, max_lun fix, free DCBs * + * for missing LUNs, pending int * + * 1.20g 98/05/19 KG Clean up: Avoid short * + * 1.20h 98/05/21 KG Remove AdaptSCSIID, max_lun ... * + * 1.20i 98/05/21 KG Aiiie: Bug with TagQMask * + * 1.20j 98/05/24 KG Handle STAT_BUSY, handle pACB->pLinkDCB * + * == 0 in remove_dev and DoingSRB_Done * + * 1.20k 98/05/25 KG DMA_INT (experimental) * + * 1.20l 98/05/27 KG remove DMA_INT; DMA_IDLE cmds added; * + * 1.20m 98/06/10 KG glitch configurable; made some global * + * vars part of ACB; use DC390_readX * + * 1.20n 98/06/11 KG startup params * + * 1.20o 98/06/15 KG added TagMaxNum to boot/module params * + * Device Nr -> Idx, TagMaxNum power of 2 * + * 1.20p 98/06/17 KG Docu updates. Reset depends on settings * + * pci_set_master added; 2.0.xx: pcibios_* * + * used instead of MechNum things ... * + * 1.20q 98/06/23 KG Changed defaults. Added debug code for * + * removable media and fixed it. TagMaxNum * + * fixed for DC390. Locking: ACB, DRV for * + * better IRQ sharing. Spelling: Queueing * + * Parsing and glitch_cfg changes. Display * + * real SyncSpeed value. Made DisConn * + * functional (!) * + * 1.20r 98/06/30 KG Debug macros, allow disabling DsCn, set * + * BIT4 in CtrlR4, EN_PAGE_INT, 2.0 module * + * param -1 fixed. * + * 1.20s 98/08/20 KG Debug info on abort(), try to check PCI,* + * phys_to_bus instead of phys_to_virt, * + * fixed sel. process, fixed locking, * + * added MODULE_XXX infos, changed IRQ * + * request flags, disable DMA_INT * + * 1.20t 98/09/07 KG TagQ report fixed; Write Erase DMA Stat;* + * initfunc -> __init; better abort; * + * Timeout for XFER_DONE & BLAST_COMPLETE; * + * Allow up to 33 commands being processed * + * 2.0a 98/10/14 KG Max Cmnds back to 17. DMA_Stat clearing * + * all flags. Clear within while() loops * + * in DataIn_0/Out_0. Null ptr in dumpinfo * + * for pSRB==0. Better locking during init.* + * bios_param() now respects part. table. * + * 2.0b 98/10/24 KG Docu fixes. Timeout Msg in DMA Blast. * + * Disallow illegal idx in INQUIRY/REMOVE * + * 2.0b1 98/11/05 KG Cleaned up detection/initialization * ***********************************************************************/ +/* Uncomment SA_INTERRUPT, if the driver refuses to share its IRQ with other devices */ +#define DC390_IRQ SA_SHIRQ /* | SA_INTERRUPT */ -#define DC390_DEBUG +/* DEBUG options */ +//#define DC390_DEBUG0 +//#define DC390_DEBUG1 +//#define DC390_DCBDEBUG +//#define DC390_PARSEDEBUG +//#define DC390_REMOVABLEDEBUG -#define SCSI_MALLOC +/* Debug definitions */ +#ifdef DC390_DEBUG0 +# define DEBUG0(x) x; +#else +# define DEBUG0(x) +#endif +#ifdef DC390_DEBUG1 +# define DEBUG1(x) x; +#else +# define DEBUG1(x) +#endif +#ifdef DC390_DCBDEBUG +# define DCBDEBUG(x) x; +#else +# define DCBDEBUG(x) +#endif +#ifdef DC390_PARSEDEBUG +# define PARSEDEBUG(x) x; +#else +# define PARSEDEBUG(x) +#endif +#ifdef DC390_REMOVABLEDEBUG +# define REMOVABLEDEBUG(x) x; +#else +# define REMOVABLEDEBUG(x) +#endif +#define DCBDEBUG1(x) +/* Includes */ #ifdef MODULE -#include +# include #endif #include @@ -46,23 +147,17 @@ #include #include #include -#include #include #include #include +#include #include #include - #include -#if LINUX_VERSION_CODE < 66354 /* 1.3.50 */ -#include "../block/blk.h" -#else #include -#endif #include "scsi.h" #include "hosts.h" -#include "tmscsim.h" #include "constants.h" #include "sd.h" #include @@ -71,112 +166,531 @@ #define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI +/* Locking */ -#ifndef VERSION_ELF_1_2_13 -struct proc_dir_entry proc_scsi_tmscsim ={ - PROC_SCSI_DC390T, 7 ,"tmscsim", - S_IFDIR | S_IRUGO | S_IXUGO, 2 - }; +/* Note: Starting from 2.1.9x, the mid-level scsi code issues a + * spinlock_irqsave (&io_request_lock) before calling the driver's + * routines, so we don't need to lock. + * TODO: Verify, if we are locked in every case! + * The policy 3, let the midlevel scsi code do the io_request_locks + * and us locking on a driver specific lock, shouldn't hurt anybody; it + * just causes a minor performance degradation for setting the locks. + */ + +/* spinlock things + * level 3: lock on both adapter specific locks and (global) io_request_lock + * level 2: lock on adapter specific locks only + * level 1: rely on the locking of the mid level code (io_request_lock) + * undef : traditional save_flags; cli; restore_flags; + */ + +//#define DEBUG_SPINLOCKS 2 /* Set to 0, 1 or 2 in include/asm/spinlock.h */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) + +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30) +# include +# include #endif -static USHORT DC390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); -static void DC390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); -static void DC390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus); - -static void SetXferRate( PACB pACB, PDCB pDCB ); -static void DC390_Disconnect( PACB pACB ); -static void DC390_Reselect( PACB pACB ); -static void SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ); -static void DoingSRB_Done( PACB pACB ); -static void DC390_ScsiRstDetect( PACB pACB ); -static void DC390_ResetSCSIBus( PACB pACB ); -static void RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ); -static void EnableMsgOut2( PACB pACB, PSRB pSRB ); -static void EnableMsgOut( PACB pACB, PSRB pSRB ); -static void DC390_InvalidCmd( PACB pACB ); -int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ); -void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ); +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,93) +# define USE_SPINLOCKS 1 +# define NEW_PCI 1 +#else +# undef NEW_PCI +# if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30) +# define USE_SPINLOCKS 2 +# endif +#endif + +#ifdef USE_SPINLOCKS + +# if USE_SPINLOCKS == 3 /* both */ + +# if defined (__SMP__) || DEBUG_SPINLOCKS > 0 +# define DC390_LOCKA_INIT { spinlock_t __unlocked = SPIN_LOCK_UNLOCKED; pACB->lock = __unlocked; }; +# else +# define DC390_LOCKA_INIT +# endif + spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED; + +# define DC390_AFLAGS unsigned long aflags; +# define DC390_IFLAGS unsigned long iflags; +# define DC390_DFLAGS unsigned long dflags; + +# define DC390_LOCK_IO spin_lock_irqsave (&io_request_lock, iflags) +# define DC390_UNLOCK_IO spin_unlock_irqrestore (&io_request_lock, iflags) + +# define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags) +# define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags) +# define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock) +# define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock) + +# define DC390_LOCK_ACB spin_lock_irqsave (&(pACB->lock), aflags) +# define DC390_UNLOCK_ACB spin_unlock_irqrestore (&(pACB->lock), aflags) +# define DC390_LOCK_ACB_NI spin_lock (&(pACB->lock)) +# define DC390_UNLOCK_ACB_NI spin_unlock (&(pACB->lock)) +//# define DC390_LOCKA_INIT spin_lock_init (&(pACB->lock)) + +# else + +# if USE_SPINLOCKS == 2 /* adapter specific locks */ + +# if defined (__SMP__) || DEBUG_SPINLOCKS > 0 +# define DC390_LOCKA_INIT { spinlock_t __unlocked = SPIN_LOCK_UNLOCKED; pACB->lock = __unlocked; }; +# else +# define DC390_LOCKA_INIT +# endif + spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED; +# define DC390_AFLAGS unsigned long aflags; +# define DC390_IFLAGS +# define DC390_DFLAGS unsigned long dflags; +# define DC390_LOCK_IO /* spin_lock_irqsave (&io_request_lock, iflags) */ +# define DC390_UNLOCK_IO /* spin_unlock_irqrestore (&io_request_lock, iflags) */ +# define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags) +# define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags) +# define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock) +# define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock) +# define DC390_LOCK_ACB spin_lock_irqsave (&(pACB->lock), aflags) +# define DC390_UNLOCK_ACB spin_unlock_irqrestore (&(pACB->lock), aflags) +# define DC390_LOCK_ACB_NI spin_lock (&(pACB->lock)) +# define DC390_UNLOCK_ACB_NI spin_unlock (&(pACB->lock)) +//# define DC390_LOCKA_INIT spin_lock_init (&(pACB->lock)) + +# else /* USE_SPINLOCKS == 1: global lock io_request_lock */ + +# define DC390_AFLAGS +# define DC390_IFLAGS unsigned long iflags; +# define DC390_DFLAGS unsigned long dflags; + spinlock_t dc390_drvlock = SPIN_LOCK_UNLOCKED; +# define DC390_LOCK_IO spin_lock_irqsave (&io_request_lock, iflags) +# define DC390_UNLOCK_IO spin_unlock_irqrestore (&io_request_lock, iflags) +# define DC390_LOCK_DRV spin_lock_irqsave (&dc390_drvlock, dflags) +# define DC390_UNLOCK_DRV spin_unlock_irqrestore (&dc390_drvlock, dflags) +# define DC390_LOCK_DRV_NI spin_lock (&dc390_drvlock) +# define DC390_UNLOCK_DRV_NI spin_unlock (&dc390_drvlock) +# define DC390_LOCK_ACB /* DC390_LOCK_IO */ +# define DC390_UNLOCK_ACB /* DC390_UNLOCK_IO */ +# define DC390_LOCK_ACB_NI /* spin_lock (&(pACB->lock)) */ +# define DC390_UNLOCK_ACB_NI /* spin_unlock (&(pACB->lock)) */ +# define DC390_LOCKA_INIT /* DC390_LOCKA_INIT */ + +# endif /* 2 */ +# endif /* 3 */ + +#else /* USE_SPINLOCKS undefined */ + +# define DC390_AFLAGS unsigned long aflags; +# define DC390_IFLAGS unsigned long iflags; +# define DC390_DFLAGS unsigned long dflags; +# define DC390_LOCK_IO save_flags (iflags); cli () +# define DC390_UNLOCK_IO restore_flags (iflags) +# define DC390_LOCK_DRV save_flags (dflags); cli () +# define DC390_UNLOCK_DRV restore_flags (dflags) +# define DC390_LOCK_DRV_NI +# define DC390_UNLOCK_DRV_NI +# define DC390_LOCK_ACB save_flags (aflags); cli () +# define DC390_UNLOCK_ACB restore_flags (aflags) +# define DC390_LOCK_ACB_NI +# define DC390_UNLOCK_ACB_NI +# define DC390_LOCKA_INIT +#endif /* def */ + + +/* These macros are used for uniform access to 2.0.x and 2.1.x PCI config space*/ + +#ifdef NEW_PCI +# define PDEV pdev +# define PDEVDECL struct pci_dev *pdev +# define PDEVDECL0 struct pci_dev *pdev = NULL +# define PDEVDECL1 struct pci_dev *pdev +# define PDEVSET pACB->pdev=pdev +# define PDEVSET1 pdev=pACB->pdev +# define PCI_WRITE_CONFIG_BYTE(pd, rv, bv) pci_write_config_byte (pd, rv, bv) +# define PCI_READ_CONFIG_BYTE(pd, rv, bv) pci_read_config_byte (pd, rv, bv) +# define PCI_WRITE_CONFIG_WORD(pd, rv, bv) pci_write_config_word (pd, rv, bv) +# define PCI_READ_CONFIG_WORD(pd, rv, bv) pci_read_config_word (pd, rv, bv) +# define PCI_BUS_DEV pdev->bus->number, pdev->devfn +# define PCI_PRESENT pci_present () +# define PCI_SET_MASTER pci_set_master (pdev) +# define PCI_FIND_DEVICE(vend, id) (pdev = pci_find_device (vend, id, pdev)) +# define PCI_GET_IO_AND_IRQ io_port = pdev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; irq = pdev->irq +#else +# include +# define PDEV pbus, pdevfn +# define PDEVDECL UCHAR pbus, UCHAR pdevfn +# define PDEVDECL0 UCHAR pbus = 0; UCHAR pdevfn = 0; USHORT pci_index = 0; int error +# define PDEVDECL1 UCHAR pbus; UCHAR pdevfn /*; USHORT pci_index */ +# define PDEVSET pACB->pbus=pbus; pACB->pdevfn=pdevfn /*; pACB->pci_index=pci_index */ +# define PDEVSET1 pbus=pACB->pbus; pdevfn=pACB->pdevfn /*; pci_index=pACB->pci_index */ +# define PCI_WRITE_CONFIG_BYTE(pd, rv, bv) pcibios_write_config_byte (pd, rv, bv) +# define PCI_READ_CONFIG_BYTE(pd, rv, bv) pcibios_read_config_byte (pd, rv, bv) +# define PCI_WRITE_CONFIG_WORD(pd, rv, bv) pcibios_write_config_word (pd, rv, bv) +# define PCI_READ_CONFIG_WORD(pd, rv, bv) pcibios_read_config_word (pd, rv, bv) +# define PCI_BUS_DEV pbus, pdevfn +# define PCI_PRESENT pcibios_present () +# define PCI_SET_MASTER dc390_set_master (pbus, pdevfn) +# define PCI_FIND_DEVICE(vend, id) (!pcibios_find_device (vend, id, pci_index++, &pbus, &pdevfn)) +# define PCI_GET_IO_AND_IRQ error = pcibios_read_config_dword (pbus, pdevfn, PCI_BASE_ADDRESS_0, &io_port); \ + error |= pcibios_read_config_byte (pbus, pdevfn, PCI_INTERRUPT_LINE, &irq); \ + io_port &= 0xfffe; \ + if (error) { printk (KERN_ERR "DC390_detect: Error reading PCI config registers!\n"); continue; } +#endif + +#include "tmscsim.h" + +#ifndef __init +# define __init +#endif + +UCHAR dc390_StartSCSI( PACB pACB, PDCB pDCB, PSRB pSRB ); +void dc390_DataOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +void dc390_DataIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_Command_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_Status_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_MsgOut_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +void dc390_MsgIn_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_DataOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_DataInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +void dc390_CommandPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_StatusPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +void dc390_MsgOutPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_MsgInPhase( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_Nop_0( PACB pACB, PSRB pSRB, PUCHAR psstatus); +static void dc390_Nop_1( PACB pACB, PSRB pSRB, PUCHAR psstatus); + +static void dc390_SetXferRate( PACB pACB, PDCB pDCB ); +void dc390_Disconnect( PACB pACB ); +void dc390_Reselect( PACB pACB ); +void dc390_SRBdone( PACB pACB, PDCB pDCB, PSRB pSRB ); +void dc390_DoingSRB_Done( PACB pACB ); +static void dc390_ScsiRstDetect( PACB pACB ); +static void dc390_ResetSCSIBus( PACB pACB ); +static void __inline__ dc390_RequestSense( PACB pACB, PDCB pDCB, PSRB pSRB ); +static void __inline__ dc390_EnableMsgOut2( PACB pACB, PSRB pSRB ); +static void __inline__ dc390_EnableMsgOut( PACB pACB, PSRB pSRB ); +static void __inline__ dc390_InvalidCmd( PACB pACB ); +static void dc390_remove_dev (PACB pACB, PDCB pDCB); +void do_DC390_Interrupt( int, void *, struct pt_regs *); + +int dc390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, UCHAR index ); +void dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd ); +void dc390_updateDCB (PACB pACB, PDCB pDCB); #ifdef MODULE -static int DC390_release(struct Scsi_Host *host); -static int DC390_shutdown (struct Scsi_Host *host); + static int DC390_release(struct Scsi_Host *host); + static int dc390_shutdown (struct Scsi_Host *host); #endif -static PSHT pSHT_start = NULL; -static PSH pSH_start = NULL; -static PSH pSH_current = NULL; -static PACB pACB_start= NULL; -static PACB pACB_current = NULL; -static PDCB pPrevDCB = NULL; -static USHORT adapterCnt = 0; -static USHORT InitialTime = 0; -static USHORT CurrSyncOffset = 0; -static ULONG mech1addr; -static UCHAR mech2bus, mech2Agent, mech2CfgSPenR; - -static PVOID DC390_phase0[]={ - DC390_DataOut_0, - DC390_DataIn_0, - DC390_Command_0, - DC390_Status_0, - DC390_Nop_0, - DC390_Nop_0, - DC390_MsgOut_0, - DC390_MsgIn_0, - DC390_Nop_1 +//static PSHT dc390_pSHT_start = NULL; +//static PSH dc390_pSH_start = NULL; +//static PSH dc390_pSH_current = NULL; +static PACB dc390_pACB_start= NULL; +static PACB dc390_pACB_current = NULL; +static UCHAR dc390_adapterCnt = 0; +static UCHAR dc390_CurrSyncOffset = 0; +static ULONG dc390_lastabortedpid = 0; +static ULONG dc390_laststatus = 0; + +#ifndef CONFIG_SCSI_DC390T_NOGENSUPP +/* Startup values, to be overriden on the commandline */ +int tmscsim[] = {7, 1 /* 8MHz */, + PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_ + | SYNC_NEGO_ | TAG_QUEUEING_, + MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION + /* | NO_SEEK */ +# ifdef CONFIG_SCSI_MULTI_LUN + | LUN_CHECK +# endif + , 3 /* 16 Tags per LUN */}; + +# if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30) +MODULE_PARM(tmscsim, "1-5i"); +MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1)"); +# endif + +#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */ + +#if defined(MODULE) && LINUX_VERSION_CODE >= LinuxVersionCode(2,1,30) +MODULE_AUTHOR("C.L. Huang / Kurt Garloff"); +MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters"); +MODULE_SUPPORTED_DEVICE("sd,sr,sg,st"); +#endif + +static PVOID dc390_phase0[]={ + dc390_DataOut_0, + dc390_DataIn_0, + dc390_Command_0, + dc390_Status_0, + dc390_Nop_0, + dc390_Nop_0, + dc390_MsgOut_0, + dc390_MsgIn_0, + dc390_Nop_1 }; -static PVOID DC390_phase1[]={ - DC390_DataOutPhase, - DC390_DataInPhase, - DC390_CommandPhase, - DC390_StatusPhase, - DC390_Nop_0, - DC390_Nop_0, - DC390_MsgOutPhase, - DC390_MsgInPhase, - DC390_Nop_1, +static PVOID dc390_phase1[]={ + dc390_DataOutPhase, + dc390_DataInPhase, + dc390_CommandPhase, + dc390_StatusPhase, + dc390_Nop_0, + dc390_Nop_0, + dc390_MsgOutPhase, + dc390_MsgInPhase, + dc390_Nop_1 }; -UCHAR eepromBuf[MAX_ADAPTER_NUM][128]; - - -UCHAR clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20}; +#ifdef DC390_DEBUG1 +static char* dc390_p0_str[] = { + "dc390_DataOut_0", + "dc390_DataIn_0", + "dc390_Command_0", + "dc390_Status_0", + "dc390_Nop_0", + "dc390_Nop_0", + "dc390_MsgOut_0", + "dc390_MsgIn_0", + "dc390_Nop_1" + }; + +static char* dc390_p1_str[] = { + "dc390_DataOutPhase", + "dc390_DataInPhase", + "dc390_CommandPhase", + "dc390_StatusPhase", + "dc390_Nop_0", + "dc390_Nop_0", + "dc390_MsgOutPhase", + "dc390_MsgInPhase", + "dc390_Nop_1" + }; +#endif -UCHAR baddevname1[2][28] ={ +/* Devices erroneously pretending to be able to do TagQ */ +UCHAR dc390_baddevname1[2][28] ={ "SEAGATE ST3390N 9546", "HP C3323-300 4269"}; - #define BADDEVCNT 2 +static char* dc390_adapname = "DC390"; +UCHAR dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN]; +UCHAR dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20}; + +struct proc_dir_entry DC390_proc_scsi_tmscsim ={ + PROC_SCSI_DC390T, 7 ,"tmscsim", + S_IFDIR | S_IRUGO | S_IXUGO, 2 + }; + /*********************************************************************** + * Functions for access to DC390 EEPROM + * and some to emulate it * - * + **********************************************************************/ + + +static void __init dc390_EnDisableCE( UCHAR mode, PDEVDECL, PUCHAR regval ) +{ + UCHAR bval; + + bval = 0; + if(mode == ENABLE_CE) + *regval = 0xc0; + else + *regval = 0x80; + PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval); + if(mode == DISABLE_CE) + PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval); + udelay(160); +} + +#ifndef CONFIG_SCSI_DC390T_NOGENSUPP +static void __init dc390_EEpromDefaults (UCHAR index) +{ + PUCHAR ptr; + UCHAR id; + ptr = (PUCHAR) dc390_eepromBuf[index]; + + /* Adapter Settings */ + ptr[EE_ADAPT_SCSI_ID] = (UCHAR)tmscsim[0]; /* Adapter ID */ + ptr[EE_MODE2] = (UCHAR)tmscsim[3]; + ptr[EE_DELAY] = 0; /* ?? */ + ptr[EE_TAG_CMD_NUM] = (UCHAR)tmscsim[4]; /* Tagged Comds */ + + /* Device Settings */ + for (id = 0; id < MAX_SCSI_ID; id++) + { + ptr[id<<2] = (UCHAR)tmscsim[2]; /* EE_MODE1 */ + ptr[(id<<2) + 1] = (UCHAR)tmscsim[1]; /* EE_Speed */ + }; + dc390_adapname = "AM53C974"; +} + +static void __init dc390_checkparams (void) +{ + PARSEDEBUG(printk(KERN_INFO "DC390: setup %08x %08x %08x %08x %08x\n", tmscsim[0],\ + tmscsim[1], tmscsim[2], tmscsim[3], tmscsim[4]);) + if (tmscsim[0] < 0 || tmscsim[0] > 7) /* modules-2.0.0 passes -1 as string */ + { + tmscsim[0] = 7; tmscsim[1] = 4; + tmscsim[2] = 9; tmscsim[3] = 15; + tmscsim[4] = 2; + printk (KERN_INFO "DC390: Using safe settings.\n"); + } + else + { + /* if (tmscsim[0] < 0 || tmscsim[0] > 7) tmscsim[0] = 7; */ + if (tmscsim[1] < 0 || tmscsim[1] > 7) tmscsim[1] = 4; + if (tmscsim[4] < 0 || tmscsim[4] > 5) tmscsim[4] = 4; + }; +}; +/* Override defaults on cmdline: + * tmscsim: AdaptID, MaxSpeed (Index), DevMode (Bitmapped), AdaptMode (Bitmapped) + */ +void __init dc390_setup (char *str, int *ints) +{ + int i; + for (i = 0; i < ints[0]; i++) + tmscsim[i] = ints[i+1]; + if (ints[0] > 5) + printk (KERN_NOTICE "DC390: ignore extra params!\n"); + /* dc390_checkparams (); */ +}; +#endif /* CONFIG_SCSI_DC390T_NOGENSUPP */ + + +static void __init dc390_EEpromOutDI( PDEVDECL, PUCHAR regval, UCHAR Carry ) +{ + UCHAR bval; + + bval = 0; + if(Carry) + { + bval = 0x40; + *regval = 0x80; + PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval); + } + udelay(160); + bval |= 0x80; + PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval); + udelay(160); + bval = 0; + PCI_WRITE_CONFIG_BYTE(PDEV, *regval, bval); + udelay(160); +} + + +static UCHAR __init dc390_EEpromInDO( PDEVDECL ) +{ + UCHAR bval; + + PCI_WRITE_CONFIG_BYTE(PDEV, 0x80, 0x80); + udelay(160); + PCI_WRITE_CONFIG_BYTE(PDEV, 0x80, 0x40); + udelay(160); + PCI_READ_CONFIG_BYTE(PDEV, 0x00, &bval); + if(bval == 0x22) + return(1); + else + return(0); +} + + +static USHORT __init dc390_EEpromGetData1( PDEVDECL ) +{ + UCHAR i; + UCHAR carryFlag; + USHORT wval; + + wval = 0; + for(i=0; i<16; i++) + { + wval <<= 1; + carryFlag = dc390_EEpromInDO(PDEV); + wval |= carryFlag; + } + return(wval); +} + + +static void __init dc390_Prepare( PDEVDECL, PUCHAR regval, UCHAR EEpromCmd ) +{ + UCHAR i,j; + UCHAR carryFlag; + + carryFlag = 1; + j = 0x80; + for(i=0; i<9; i++) + { + dc390_EEpromOutDI(PDEV,regval,carryFlag); + carryFlag = (EEpromCmd & j) ? 1 : 0; + j >>= 1; + } +} + + +static void __init dc390_ReadEEprom( PDEVDECL, PUSHORT ptr) +{ + UCHAR regval,cmd; + UCHAR i; + + cmd = EEPROM_READ; + for(i=0; i<0x40; i++) + { + dc390_EnDisableCE(ENABLE_CE, PDEV, ®val); + dc390_Prepare(PDEV, ®val, cmd++); + *ptr++ = dc390_EEpromGetData1(PDEV); + dc390_EnDisableCE(DISABLE_CE, PDEV, ®val); + } +} + + +static UCHAR __init dc390_CheckEEpromCheckSum( PDEVDECL, UCHAR index ) +{ + UCHAR i; + char EEbuf[128]; + USHORT wval, *ptr = (PUSHORT)EEbuf; + + dc390_ReadEEprom( PDEV, ptr ); + memcpy (dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID); + memcpy (&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID], + &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID); + wval = 0; + for(i=0; i<0x40; i++, ptr++) + wval += *ptr; + return (wval == 0x1234 ? 0 : 1); +} + + +/*********************************************************************** + * Functions for the management of the internal structures + * (DCBs, SRBs, Queueing) * **********************************************************************/ -static void -QLinkcmd( PSCSICMD cmd, PDCB pDCB ) +static PDCB __inline__ dc390_findDCB ( PACB pACB, Scsi_Cmnd *cmd) { - ULONG flags; - PSCSICMD pcmd; + PDCB pDCB = pACB->pLinkDCB; if (!pDCB) return 0; + while (pDCB->UnitSCSIID != cmd->target || pDCB->UnitSCSILUN != cmd->lun) + { + pDCB = pDCB->pNextDCB; + if (pDCB == pACB->pLinkDCB) + { + printk (KERN_ERR "DC390: DCB not found (DCB=%08x, DCBmap[%2x]=%2x)\n", + (int)pDCB, cmd->target, pACB->DCBmap[cmd->target]); + return 0; + } + }; + DCBDEBUG1( printk (KERN_DEBUG "DCB %08x (%02x,%02x) found.\n", \ + (int)pDCB, pDCB->UnitSCSIID, pDCB->UnitSCSILUN);) + return pDCB; +}; - save_flags(flags); - cli(); +static void dc390_QLinkcmd( PSCSICMD cmd, PDCB pDCB ) +{ + PSCSICMD pcmd; if( !pDCB->QIORBCnt ) { @@ -194,83 +708,61 @@ cmd->next = NULL; } - restore_flags(flags); } -static PSCSICMD -Getcmd( PDCB pDCB ) +static __inline__ PSCSICMD dc390_Getcmd( PDCB pDCB ) { - ULONG flags; PSCSICMD pcmd; - save_flags(flags); - cli(); - pcmd = pDCB->pQIORBhead; pDCB->pQIORBhead = pcmd->next; pcmd->next = NULL; pDCB->QIORBCnt--; - restore_flags(flags); return( pcmd ); } -static PSRB -GetSRB( PACB pACB ) +static __inline__ PSRB dc390_GetSRB( PACB pACB ) { - ULONG flags; PSRB pSRB; - save_flags(flags); - cli(); - pSRB = pACB->pFreeSRB; if( pSRB ) { pACB->pFreeSRB = pSRB->pNextSRB; pSRB->pNextSRB = NULL; } - restore_flags(flags); + return( pSRB ); } -static void -RewaitSRB0( PDCB pDCB, PSRB pSRB ) +static __inline__ void dc390_RewaitSRB0( PDCB pDCB, PSRB pSRB ) { PSRB psrb1; - ULONG flags; - - save_flags(flags); - cli(); if( (psrb1 = pDCB->pWaitingSRB) ) { pSRB->pNextSRB = psrb1; - pDCB->pWaitingSRB = pSRB; } else { pSRB->pNextSRB = NULL; - pDCB->pWaitingSRB = pSRB; pDCB->pWaitLast = pSRB; } - restore_flags(flags); + pDCB->pWaitingSRB = pSRB; } -static void -RewaitSRB( PDCB pDCB, PSRB pSRB ) +static void dc390_RewaitSRB( PDCB pDCB, PSRB pSRB ) { PSRB psrb1; - ULONG flags; UCHAR bval; - save_flags(flags); - cli(); - pDCB->GoingSRBCnt--; + pDCB->GoingSRBCnt--; pDCB->pDCBACB->SelLost++; + DEBUG0(printk("DC390: RewaitSRB (%p, %p) pid = %li\n", pDCB, pSRB, pSRB->pcmd->pid);) psrb1 = pDCB->pGoingSRB; if( pSRB == psrb1 ) { @@ -298,20 +790,14 @@ bval = pSRB->TagNumber; pDCB->TagMask &= (~(1 << bval)); /* Free TAG number */ - restore_flags(flags); } -static void -DoWaitingSRB( PACB pACB ) +static void dc390_DoWaitingSRB( PACB pACB ) { - ULONG flags; PDCB ptr, ptr1; PSRB pSRB; - save_flags(flags); - cli(); - if( !(pACB->pActiveDCB) && !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) ) { ptr = pACB->pDCBRunRobin; @@ -333,7 +819,7 @@ } else { - if( !DC390_StartSCSI(pACB, ptr1, pSRB) ) + if( !dc390_StartSCSI(pACB, ptr1, pSRB) ) { ptr1->GoingSRBCnt++; if( ptr1->pWaitLast == pSRB ) @@ -357,55 +843,54 @@ } } } - restore_flags(flags); return; } -static void -SRBwaiting( PDCB pDCB, PSRB pSRB) +static __inline__ void dc390_SRBwaiting( PDCB pDCB, PSRB pSRB) { if( pDCB->pWaitingSRB ) { pDCB->pWaitLast->pNextSRB = pSRB; - pDCB->pWaitLast = pSRB; pSRB->pNextSRB = NULL; } else { pDCB->pWaitingSRB = pSRB; - pDCB->pWaitLast = pSRB; } + pDCB->pWaitLast = pSRB; } -static void -SendSRB( PSCSICMD pcmd, PACB pACB, PSRB pSRB ) +/*********************************************************************** + * Function: static void dc390_SendSRB (PACB pACB, PSRB pSRB) + * + * Purpose: Send SCSI Request Block (pSRB) to adapter (pACB) + * + ***********************************************************************/ + +static void dc390_SendSRB( PACB pACB, PSRB pSRB ) { - ULONG flags; PDCB pDCB; - save_flags(flags); - cli(); - pDCB = pSRB->pSRBDCB; if( !(pDCB->MaxCommand > pDCB->GoingSRBCnt) || (pACB->pActiveDCB) || (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV)) ) { - SRBwaiting(pDCB, pSRB); + dc390_SRBwaiting(pDCB, pSRB); goto SND_EXIT; } if( pDCB->pWaitingSRB ) { - SRBwaiting(pDCB, pSRB); -/* pSRB = GetWaitingSRB(pDCB); */ + dc390_SRBwaiting(pDCB, pSRB); +/* pSRB = GetWaitingSRB(pDCB); */ /* non-existent */ pSRB = pDCB->pWaitingSRB; pDCB->pWaitingSRB = pSRB->pNextSRB; pSRB->pNextSRB = NULL; } - if( !DC390_StartSCSI(pACB, pDCB, pSRB) ) + if( !dc390_StartSCSI(pACB, pDCB, pSRB) ) { pDCB->GoingSRBCnt++; if( pDCB->pGoingSRB ) @@ -420,13 +905,60 @@ } } else - RewaitSRB0( pDCB, pSRB ); + dc390_RewaitSRB0( pDCB, pSRB ); SND_EXIT: - restore_flags(flags); return; } +/*********************************************************************** + * Function: static void dc390_BuildSRB (Scsi_Cmd *pcmd, PDCB pDCB, + * PSRB pSRB) + * + * Purpose: Prepare SRB for being sent to Device DCB w/ command *pcmd + * + ***********************************************************************/ + +static void dc390_BuildSRB (Scsi_Cmnd* pcmd, PDCB pDCB, PSRB pSRB) +{ + pSRB->pSRBDCB = pDCB; + pSRB->pcmd = pcmd; + pSRB->ScsiCmdLen = pcmd->cmd_len; + memcpy (pSRB->CmdBlock, pcmd->cmnd, pcmd->cmd_len); + + if( pcmd->use_sg ) + { + pSRB->SGcount = (UCHAR) pcmd->use_sg; + pSRB->pSegmentList = (PSGL) pcmd->request_buffer; + } + else if( pcmd->request_buffer ) + { + pSRB->SGcount = 1; + pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; + pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; + pSRB->Segmentx.length = pcmd->request_bufflen; + } + else + pSRB->SGcount = 0; + + pSRB->SGIndex = 0; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; + pSRB->MsgCnt = 0; + if( pDCB->DevType != TYPE_TAPE ) + pSRB->RetryCnt = 1; + else + pSRB->RetryCnt = 0; + pSRB->SRBStatus = 0; + pSRB->SRBFlag = 0; + pSRB->SRBState = 0; + pSRB->TotalXferredLen = 0; + pSRB->SGBusAddr = 0; + pSRB->SGToBeXferLen = 0; + pSRB->ScsiPhase = 0; + pSRB->EndMessage = 0; +}; + /*********************************************************************** * Function : static int DC390_queue_command (Scsi_Cmnd *cmd, @@ -434,276 +966,305 @@ * * Purpose : enqueues a SCSI command * - * Inputs : cmd - SCSI command, done - function called on completion, with - * a pointer to the command descriptor. + * Inputs : cmd - SCSI command, done - callback function called on + * completion, with a pointer to the command descriptor. * - * Returns : 0 + * Returns : (depending on kernel version) + * 2.0.x: always return 0 + * 2.1.x: old model: (use_new_eh_code == 0): like 2.0.x + * TO BE DONE: + * new model: return 0 if successful + * return 1 if command cannot be queued (queue full) + * command will be inserted in midlevel queue then ... * ***********************************************************************/ -int -DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) +int DC390_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) { - USHORT ioport, i; Scsi_Cmnd *pcmd; - struct Scsi_Host *psh; - PACB pACB; PDCB pDCB; PSRB pSRB; - ULONG flags; - PUCHAR ptr,ptr1; + DC390_AFLAGS + PACB pACB = (PACB) cmd->host->hostdata; - psh = cmd->host; - pACB = (PACB ) psh->hostdata; - ioport = pACB->IOPortBase; -#ifdef DC390_DEBUG0 -/* if(pACB->scan_devices) */ - printk("Cmd=%2x,ID=%d,LUN=%d,",cmd->cmnd[0],cmd->target,cmd->lun); -#endif + DEBUG0(/* if(pACB->scan_devices) */ \ + printk(KERN_DEBUG "DC390: Queue Cmd=%02x,ID=%d,LUN=%d (pid=%li)\n",\ + cmd->cmnd[0],cmd->target,cmd->lun,cmd->pid);) + + DC390_LOCK_ACB; + + /* Assume BAD_TARGET; will be cleared later */ + cmd->result = DID_BAD_TARGET << 16; + + /* TODO: Change the policy: Alway accept TEST_UNIT_READY or INQUIRY + * commands and alloc a DCB for the device if not yet there. DCB will + * be removed in dc390_SRBdone if SEL_TIMEOUT */ if( (pACB->scan_devices == END_SCAN) && (cmd->cmnd[0] != INQUIRY) ) - { pACB->scan_devices = 0; - pPrevDCB->pNextDCB = pACB->pLinkDCB; - } - else if( (pACB->scan_devices) && (cmd->cmnd[0] == 8) ) - { + + else if( (pACB->scan_devices) && (cmd->cmnd[0] == READ_6) ) pACB->scan_devices = 0; - pPrevDCB->pNextDCB = pACB->pLinkDCB; - } - if ( ( cmd->target > pACB->max_id ) || (cmd->lun > pACB->max_lun) ) + if ( ( cmd->target >= pACB->pScsiHost->max_id ) || + (cmd->lun >= pACB->pScsiHost->max_lun) ) { /* printk("DC390: Ignore target %d lun %d\n", cmd->target, cmd->lun); */ - cmd->result = (DID_BAD_TARGET << 16); + DC390_UNLOCK_ACB; done(cmd); return( 0 ); } - if( (pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) + if( (pACB->scan_devices || cmd->cmnd[0] == TEST_UNIT_READY) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) { - if( pACB->DeviceCnt < MAX_DEVICES ) - { - pACB->DCBmap[cmd->target] |= (1 << cmd->lun); - pDCB = pACB->pDCB_free; -#ifdef DC390_DEBUG0 - printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target); -#endif - DC390_initDCB( pACB, pDCB, cmd ); - } - else /* ???? */ - { -/* printk("DC390: Ignore target %d lun %d\n", - cmd->target, cmd->lun); */ - cmd->result = (DID_BAD_TARGET << 16); + pACB->DCBmap[cmd->target] |= (1 << cmd->lun); + pACB->scan_devices = 1; + + dc390_initDCB( pACB, &pDCB, cmd ); + if (!pDCB) + { + printk (KERN_ERR "DC390: kmalloc for DCB failed, ID=%2x\n", cmd->target); + DC390_UNLOCK_ACB; done(cmd); return(0); - } + }; + } else if( !(pACB->scan_devices) && !(pACB->DCBmap[cmd->target] & (1 << cmd->lun)) ) { -/* printk("DC390: Ignore target %d lun %d\n", - cmd->target, cmd->lun); */ - cmd->result = (DID_BAD_TARGET << 16); + printk(KERN_INFO "DC390: Ignore target %02x lun %02x\n", + cmd->target, cmd->lun); + DC390_UNLOCK_ACB; done(cmd); return(0); } else { - pDCB = pACB->pLinkDCB; - while( (pDCB->UnitSCSIID != cmd->target) || - (pDCB->UnitSCSILUN != cmd->lun) ) - { - pDCB = pDCB->pNextDCB; - } -#ifdef DC390_DEBUG0 - printk("pDCB=%8x,ID=%2x,", (UINT) pDCB, cmd->target); -#endif + pDCB = dc390_findDCB (pACB, cmd); + if (!pDCB) + { /* should never happen */ + DC390_UNLOCK_ACB; + done(cmd); + return(0); + }; } + pACB->Cmds++; cmd->scsi_done = done; cmd->result = 0; - save_flags(flags); - cli(); - - if( pDCB->QIORBCnt ) + if( pDCB->QIORBCnt ) /* Unsent commands ? */ { - QLinkcmd( cmd, pDCB ); - pcmd = Getcmd( pDCB ); + dc390_QLinkcmd( cmd, pDCB ); + pcmd = dc390_Getcmd( pDCB ); /* Get first command */ + pACB->CmdInQ++; } else pcmd = cmd; - pSRB = GetSRB( pACB ); + pSRB = dc390_GetSRB( pACB ); if( !pSRB ) { - QLinkcmd( pcmd, pDCB ); - restore_flags(flags); + dc390_QLinkcmd( pcmd, pDCB ); /* Queue command at the end */ + pACB->CmdOutOfSRB++; + DC390_UNLOCK_ACB; return(0); } -/* BuildSRB(pSRB); */ + dc390_BuildSRB (pcmd, pDCB, pSRB); + dc390_SendSRB( pACB, pSRB ); - pSRB->pSRBDCB = pDCB; - pSRB->pcmd = pcmd; - ptr = (PUCHAR) pSRB->CmdBlock; - ptr1 = (PUCHAR) pcmd->cmnd; - pSRB->ScsiCmdLen = pcmd->cmd_len; - for(i=0; i< pcmd->cmd_len; i++) + DC390_UNLOCK_ACB; + DEBUG1(printk (KERN_DEBUG " ... command (%02x) queued successfully.\n", pcmd->cmnd[0]);) + return(0); +} + + +static void dc390_DoNextCmd( PACB pACB, PDCB pDCB ) +{ + Scsi_Cmnd *pcmd; + PSRB pSRB; + + if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) + return; + + pcmd = dc390_Getcmd( pDCB ); + pSRB = dc390_GetSRB( pACB ); + if( !pSRB ) + dc390_QLinkcmd( pcmd, pDCB ); + else + { + dc390_BuildSRB (pcmd, pDCB, pSRB); + dc390_SendSRB( pACB, pSRB ); + }; +} + +/* We ignore mapping problems, as we expect everybody to respect + * valid partition tables. Waiting for complaints ;-) */ + +#ifdef CONFIG_SCSI_DC390T_TRADMAP +/* + * The next function, partsize(), is copied from scsicam.c. + * + * This is ugly code duplication, but I didn't find another way to solve it: + * We want to respect the partition table and if it fails, we apply the + * DC390 BIOS heuristic. Too bad, just calling scsicam_bios_param() doesn't do + * the job, because we don't know, whether the values returned are from + * the part. table or determined by setsize(). Unfortunately the setsize() + * values differ from the ones chosen by the DC390 BIOS. + * + * Looking forward to seeing suggestions for a better solution! KG, 98/10/14 + */ +#include + +/* + * Function : static int partsize(struct buffer_head *bh, unsigned long + * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs); + * + * Purpose : to determine the BIOS mapping used to create the partition + * table, storing the results in *cyls, *hds, and *secs + * + * Returns : -1 on failure, 0 on success. + * + */ + +static int partsize(struct buffer_head *bh, unsigned long capacity, + unsigned int *cyls, unsigned int *hds, unsigned int *secs) { + struct partition *p, *largest = NULL; + int i, largest_cyl; + int cyl, ext_cyl, end_head, end_cyl, end_sector; + unsigned int logical_end, physical_end, ext_physical_end; + + + if (*(unsigned short *) (bh->b_data+510) == 0xAA55) { + for (largest_cyl = -1, p = (struct partition *) + (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) { + if (!p->sys_ind) + continue; + cyl = p->cyl + ((p->sector & 0xc0) << 2); + if (cyl > largest_cyl) { + largest_cyl = cyl; + largest = p; + } + } + } + + if (largest) { + end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2); + end_head = largest->end_head; + end_sector = largest->end_sector & 0x3f; + + physical_end = end_cyl * (end_head + 1) * end_sector + + end_head * end_sector + end_sector; + + /* This is the actual _sector_ number at the end */ + logical_end = get_unaligned(&largest->start_sect) + + get_unaligned(&largest->nr_sects); + + /* This is for >1023 cylinders */ + ext_cyl= (logical_end-(end_head * end_sector + end_sector)) + /(end_head + 1) / end_sector; + ext_physical_end = ext_cyl * (end_head + 1) * end_sector + + end_head * end_sector + end_sector; + + if ((logical_end == physical_end) || + (end_cyl==1023 && ext_physical_end==logical_end)) { + *secs = end_sector; + *hds = end_head + 1; + *cyls = capacity / ((end_head + 1) * end_sector); + return 0; + } + } + return -1; +} + +/*********************************************************************** + * Function: + * DC390_bios_param + * + * Description: + * Return the disk geometry for the given SCSI device. + * Respect the partition table, otherwise try own heuristic + * + * Note: + * In contrary to other externally callable funcs (DC390_), we don't lock + ***********************************************************************/ +int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) +{ + int heads, sectors, cylinders; + PACB pACB = (PACB) disk->device->host->hostdata; + struct buffer_head *bh; + int ret_code = -1; + int size = disk->capacity; + + if ((bh = bread(MKDEV(MAJOR(devno), MINOR(devno)&~0xf), 0, 1024))) { - *ptr = *ptr1; - ptr++; - ptr1++; + /* try to infer mapping from partition table */ + ret_code = partsize (bh, (unsigned long) size, (unsigned int *) geom + 2, + (unsigned int *) geom + 0, (unsigned int *) geom + 1); + brelse (bh); } - if( pcmd->use_sg ) + if (ret_code == -1) { - pSRB->SGcount = (UCHAR) pcmd->use_sg; - pSRB->pSegmentList = (PSGL) pcmd->request_buffer; - } - else if( pcmd->request_buffer ) - { - pSRB->SGcount = 1; - pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; - pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; - pSRB->Segmentx.length = pcmd->request_bufflen; - } - else - pSRB->SGcount = 0; - - pSRB->SGIndex = 0; - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; - pSRB->MsgCnt = 0; - if( pDCB->DevType != TYPE_TAPE ) - pSRB->RetryCnt = 1; - else - pSRB->RetryCnt = 0; - pSRB->SRBStatus = 0; - pSRB->SRBFlag = 0; - pSRB->SRBState = 0; - pSRB->TotalXferredLen = 0; - pSRB->SGPhysAddr = 0; - pSRB->SGToBeXferLen = 0; - pSRB->ScsiPhase = 0; - pSRB->EndMessage = 0; - SendSRB( pcmd, pACB, pSRB ); - - restore_flags(flags); - return(0); -} + heads = 64; + sectors = 32; + cylinders = size / (heads * sectors); + if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) ) + { + heads = 255; + sectors = 63; + cylinders = size / (heads * sectors); + } -static void -DoNextCmd( PACB pACB, PDCB pDCB ) -{ - Scsi_Cmnd *pcmd; - PSRB pSRB; - ULONG flags; - PUCHAR ptr,ptr1; - USHORT i; - - - if( pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV) ) - return; - save_flags(flags); - cli(); - - pcmd = Getcmd( pDCB ); - pSRB = GetSRB( pACB ); - if( !pSRB ) - { - QLinkcmd( pcmd, pDCB ); - restore_flags(flags); - return; - } - - pSRB->pSRBDCB = pDCB; - pSRB->pcmd = pcmd; - ptr = (PUCHAR) pSRB->CmdBlock; - ptr1 = (PUCHAR) pcmd->cmnd; - pSRB->ScsiCmdLen = pcmd->cmd_len; - for(i=0; i< pcmd->cmd_len; i++) - { - *ptr = *ptr1; - ptr++; - ptr1++; - } - if( pcmd->use_sg ) - { - pSRB->SGcount = (UCHAR) pcmd->use_sg; - pSRB->pSegmentList = (PSGL) pcmd->request_buffer; - } - else if( pcmd->request_buffer ) - { - pSRB->SGcount = 1; - pSRB->pSegmentList = (PSGL) &pSRB->Segmentx; - pSRB->Segmentx.address = (PUCHAR) pcmd->request_buffer; - pSRB->Segmentx.length = pcmd->request_bufflen; + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; } - else - pSRB->SGcount = 0; - pSRB->SGIndex = 0; - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; - pSRB->MsgCnt = 0; - if( pDCB->DevType != TYPE_TAPE ) - pSRB->RetryCnt = 1; - else - pSRB->RetryCnt = 0; - pSRB->SRBStatus = 0; - pSRB->SRBFlag = 0; - pSRB->SRBState = 0; - pSRB->TotalXferredLen = 0; - pSRB->SGPhysAddr = 0; - pSRB->SGToBeXferLen = 0; - pSRB->ScsiPhase = 0; - pSRB->EndMessage = 0; - SendSRB( pcmd, pACB, pSRB ); - - restore_flags(flags); - return; + return (0); } - - -/*********************************************************************** - * Function: - * DC390_bios_param - * - * Description: - * Return the disk geometry for the given SCSI device. - ***********************************************************************/ -#ifdef VERSION_ELF_1_2_13 -int DC390_bios_param(Disk *disk, int devno, int geom[]) #else -int DC390_bios_param(Disk *disk, kdev_t devno, int geom[]) -#endif +int DC390_bios_param (Disk *disk, kdev_t devno, int geom[]) { - int heads, sectors, cylinders; - PACB pACB; - - pACB = (PACB) disk->device->host->hostdata; - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); - - if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) ) - { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } + return scsicam_bios_param (disk, devno, geom); +}; +#endif - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - return (0); -} +void dc390_dumpinfo (PACB pACB, PDCB pDCB, PSRB pSRB) +{ + USHORT pstat; PDEVDECL1; + + if (pSRB) + { + printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08lx, Phase %02x\n", + pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState, + pSRB->ScsiPhase); + printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus); + }; + printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08lx\n", dc390_laststatus); + printk ("DC390: Register dump: SCSI block:\n"); + printk ("DC390: XferCnt Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n"); + printk ("DC390: %06x %02x %02x %02x", + DC390_read8(CtcReg_Low) + (DC390_read8(CtcReg_Mid) << 8) + (DC390_read8(CtcReg_High) << 16), + DC390_read8(ScsiCmd), DC390_read8(Scsi_Status), DC390_read8(Intern_State)); + printk (" %02x %02x %02x %02x %02x %02x\n", + DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1), + DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4)); + printk ("DC390: Register dump: DMA engine:\n"); + printk ("DC390: Cmd STrCnt SBusA WrkBC WrkAC Stat SBusCtrl\n"); + printk ("DC390: %02x %08x %08x %08x %08x %02x %08x\n", + DC390_read8(DMA_Cmd), DC390_read32(DMA_XferCnt), DC390_read32(DMA_XferAddr), + DC390_read32(DMA_Wk_ByteCntr), DC390_read32(DMA_Wk_AddrCntr), + DC390_read8(DMA_Status), DC390_read32(DMA_ScsiBusCtrl)); + PDEVSET1; PCI_READ_CONFIG_WORD(PDEV, PCI_STATUS, &pstat); + printk ("DC390: Register dump: PCI Status: %04x\n", pstat); + printk ("DC390: Please report driver trouble to K.Garloff@ping.de\n"); +}; /*********************************************************************** @@ -714,37 +1275,32 @@ * Inputs : cmd - command to abort * * Returns : 0 on success, -1 on failure. + * + * Status: Buggy ! ***********************************************************************/ -int -DC390_abort (Scsi_Cmnd *cmd) +int DC390_abort (Scsi_Cmnd *cmd) { - ULONG flags; - PACB pACB; - PDCB pDCB, pdcb; + PDCB pDCB; PSRB pSRB, psrb; - USHORT count, i; + ULONG count, i; PSCSICMD pcmd, pcmd1; int status; + ULONG sbac; + DC390_AFLAGS + PACB pACB = (PACB) cmd->host->hostdata; + + DC390_LOCK_ACB; + + pDCB = dc390_findDCB (pACB, cmd); + /* abort() is too buggy at the moment. If it's called we are in trouble anyway. + * so let's dump some info into the syslog at least. (KG, 98/08/20) */ + if (pDCB) pSRB = pDCB->pActiveSRB; else pSRB = 0; + printk ("DC390: Abort command (pid %li, DCB %p, SRB %p)\n", + cmd->pid, pDCB, pSRB); + dc390_dumpinfo (pACB, pDCB, pSRB); - -#ifdef DC390_DEBUG0 - printk("DC390 : Abort Cmd."); -#endif - - save_flags(flags); - cli(); - - pACB = (PACB) cmd->host->hostdata; - pDCB = pACB->pLinkDCB; - pdcb = pDCB; - while( (pDCB->UnitSCSIID != cmd->target) || - (pDCB->UnitSCSILUN != cmd->lun) ) - { - pDCB = pDCB->pNextDCB; - if( pDCB == pdcb ) - goto NOT_RUN; - } + if( !pDCB ) goto NOT_RUN; if( pDCB->QIORBCnt ) { @@ -774,7 +1330,12 @@ } } } - + + /* Added 98/07/02 KG */ + pSRB = pDCB->pActiveSRB; + if (pSRB && pSRB->pcmd == cmd ) + goto ON_GOING; + pSRB = pDCB->pWaitingSRB; if( !pSRB ) goto ON_GOING; @@ -791,7 +1352,7 @@ while( psrb->pNextSRB->pcmd != cmd ) { psrb = psrb->pNextSRB; - if( !(psrb->pNextSRB) ) + if( !(psrb->pNextSRB) || psrb == pSRB) goto ON_GOING; } pSRB = psrb->pNextSRB; @@ -808,6 +1369,8 @@ ON_GOING: pSRB = pDCB->pGoingSRB; + pDCB->DCBFlag |= ABORT_DEV_; + /* Now for the hard part: The command is currently processed */ for( count = pDCB->GoingSRBCnt, i=0; ipcmd != cmd ) @@ -817,6 +1380,8 @@ if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) ) { status = SCSI_ABORT_BUSY; + printk ("DC390: Abort current command (pid %li, SRB %p)\n", + cmd->pid, pSRB); goto ABO_X; } else @@ -832,20 +1397,51 @@ ABO_X: cmd->result = DID_ABORT << 16; + printk(KERN_INFO "DC390: Aborted pid %li with status %i\n", cmd->pid, status); + if (cmd->pid == dc390_lastabortedpid) /* repeated failure ? */ + { + /* Let's do something to help the bus getting clean again */ + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + DC390_write8 (ScsiCmd, DMA_COMMAND); + //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); + //DC390_write8 (ScsiCmd, RESET_ATN_CMD); + DC390_write8 (ScsiCmd, NOP_CMD); + //udelay (10000); + //DC390_read8 (INT_Status); + //DC390_write8 (ScsiCmd, EN_SEL_RESEL); + }; + sbac = DC390_read32 (DMA_ScsiBusCtrl); + if (sbac & SCSI_BUSY) + { /* clear BSY, SEL and ATN */ + printk (KERN_WARNING "DC390: Reset SCSI device: "); + //DC390_write32 (DMA_ScsiBusCtrl, (sbac | SCAM) & ~SCSI_LINES); + //udelay (250); + //sbac = DC390_read32 (DMA_ScsiBusCtrl); + //printk ("%08lx ", sbac); + //DC390_write32 (DMA_ScsiBusCtrl, sbac & ~(SCSI_LINES | SCAM)); + //udelay (100); + //sbac = DC390_read32 (DMA_ScsiBusCtrl); + //printk ("%08lx ", sbac); + DC390_write8 (ScsiCmd, RST_DEVICE_CMD); + udelay (250); + DC390_write8 (ScsiCmd, NOP_CMD); + sbac = DC390_read32 (DMA_ScsiBusCtrl); + printk ("%08lx\n", sbac); + }; + dc390_lastabortedpid = cmd->pid; + DC390_UNLOCK_ACB; + //do_DC390_Interrupt (pACB->IRQLevel, 0, 0); cmd->scsi_done(cmd); - restore_flags(flags); return( status ); } -static void -ResetDevParam( PACB pACB ) +static void dc390_ResetDevParam( PACB pACB ) { PDCB pDCB, pdcb; pDCB = pACB->pLinkDCB; - if( pDCB == NULL ) - return; + if (! pDCB) return; pdcb = pDCB; do { @@ -853,24 +1449,22 @@ pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; pDCB->CtrlR3 = FAST_CLK; - pDCB->CtrlR4 &= NEGATE_REQACKDATA; - pDCB->CtrlR4 |= EATER_25NS; + pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK; + pDCB->CtrlR4 |= pACB->glitch_cfg; pDCB = pDCB->pNextDCB; } while( pdcb != pDCB ); } -static void -RecoverSRB( PACB pACB ) +static void dc390_RecoverSRB( PACB pACB ) { PDCB pDCB, pdcb; PSRB psrb, psrb2; - USHORT cnt, i; + ULONG cnt, i; pDCB = pACB->pLinkDCB; - if( pDCB == NULL ) - return; + if( !pDCB ) return; pdcb = pDCB; do { @@ -880,7 +1474,7 @@ { psrb2 = psrb; psrb = psrb->pNextSRB; -/* RewaitSRB( pDCB, psrb ); */ +/* dc390_RewaitSRB( pDCB, psrb ); */ if( pdcb->pWaitingSRB ) { psrb2->pNextSRB = pdcb->pWaitingSRB; @@ -897,8 +1491,7 @@ pdcb->pGoingSRB = NULL; pdcb->TagMask = 0; pdcb = pdcb->pNextDCB; - } - while( pdcb != pDCB ); + } while( pdcb != pDCB ); } @@ -912,83 +1505,82 @@ * Returns : 0 on success. ***********************************************************************/ -#ifdef VERSION_2_0_0 int DC390_reset(Scsi_Cmnd *cmd, unsigned int resetFlags) -#else -int DC390_reset (Scsi_Cmnd *cmd) -#endif { - USHORT ioport; - unsigned long flags; - PACB pACB; - UCHAR bval; - USHORT i; + UCHAR bval; + ULONG i; + DC390_AFLAGS + PACB pACB = (PACB) cmd->host->hostdata; + printk(KERN_INFO "DC390: RESET ... "); -#ifdef DC390_DEBUG1 - printk("DC390: RESET,"); -#endif - - pACB = (PACB ) cmd->host->hostdata; - ioport = pACB->IOPortBase; - save_flags(flags); - cli(); - bval = inb(ioport+CtrlReg1); + DC390_LOCK_ACB; + bval = DC390_read8 (CtrlReg1); bval |= DIS_INT_ON_SCSI_RST; - outb(bval,ioport+CtrlReg1); /* disable interrupt */ - DC390_ResetSCSIBus( pACB ); - for( i=0; i<500; i++ ) + DC390_write8 (CtrlReg1, bval); /* disable interrupt */ + + dc390_ResetSCSIBus( pACB ); + /* Unlock ? */ + for( i=0; i<600; i++ ) udelay(1000); - bval = inb(ioport+CtrlReg1); - bval &= ~DIS_INT_ON_SCSI_RST; - outb(bval,ioport+CtrlReg1); /* re-enable interrupt */ - bval = DMA_IDLE_CMD; - outb(bval,ioport+DMA_Cmd); - bval = CLEAR_FIFO_CMD; - outb(bval,ioport+ScsiCmd); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); + DC390_read8 (INT_Status); /* Reset Pending INT */ - ResetDevParam( pACB ); - DoingSRB_Done( pACB ); + dc390_ResetDevParam( pACB ); + dc390_DoingSRB_Done( pACB ); + /* dc390_RecoverSRB (pACB); */ pACB->pActiveDCB = NULL; pACB->ACBFlag = 0; - DoWaitingSRB( pACB ); + bval = DC390_read8 (CtrlReg1); + bval &= ~DIS_INT_ON_SCSI_RST; + DC390_write8 (CtrlReg1, bval); /* re-enable interrupt */ - restore_flags(flags); -#ifdef DC390_DEBUG1 - printk("DC390: RESET1,"); -#endif + dc390_DoWaitingSRB( pACB ); + + DC390_UNLOCK_ACB; + printk("done\n"); return( SCSI_RESET_SUCCESS ); } - #include "scsiiom.c" /*********************************************************************** - * Function : static void DC390_initDCB + * Function : static void dc390_initDCB() * * Purpose : initialize the internal structures for a given DCB * * Inputs : cmd - pointer to this scsi cmd request block structure - * ***********************************************************************/ -void DC390_initDCB( PACB pACB, PDCB pDCB, PSCSICMD cmd ) + +void dc390_initDCB( PACB pACB, PDCB *ppDCB, PSCSICMD cmd ) { PEEprom prom; - UCHAR bval; - USHORT index; + UCHAR index; + PDCB pDCB; - if( pACB->DeviceCnt == 0 ) + pDCB = kmalloc (sizeof(DC390_DCB), GFP_ATOMIC); + DCBDEBUG(printk (KERN_INFO "DC390: alloc mem for DCB (ID %i, LUN %i): 0x%08x\n", \ + cmd->target, cmd->lun, (int)pDCB);) + + *ppDCB = pDCB; + if (!pDCB) return; + if( pACB->DCBCnt == 0 ) { pACB->pLinkDCB = pDCB; pACB->pDCBRunRobin = pDCB; - pDCB->pNextDCB = pDCB; - pPrevDCB = pDCB; } else - pPrevDCB->pNextDCB = pDCB; + { + pACB->pLastDCB->pNextDCB = pDCB; + }; + + pACB->DCBCnt++; + + pACB->pLastDCB = pDCB; + pDCB->pNextDCB = pACB->pLinkDCB; pDCB->pDCBACB = pACB; pDCB->QIORBCnt = 0; @@ -1000,128 +1592,144 @@ pDCB->pActiveSRB = NULL; pDCB->TagMask = 0; pDCB->MaxCommand = 1; - pDCB->AdaptIndex = pACB->AdapterIndex; index = pACB->AdapterIndex; pDCB->DCBFlag = 0; - prom = (PEEprom) &eepromBuf[index][cmd->target << 2]; + prom = (PEEprom) &dc390_eepromBuf[index][cmd->target << 2]; pDCB->DevMode = prom->EE_MODE1; - pDCB->AdpMode = eepromBuf[index][EE_MODE2]; - - if( pDCB->DevMode & EN_DISCONNECT_ ) - bval = 0xC0; - else - bval = 0x80; - bval |= cmd->lun; - pDCB->IdentifyMsg = bval; pDCB->SyncMode = 0; - if( pDCB->DevMode & SYNC_NEGO_ ) - { - if( !(cmd->lun) || CurrSyncOffset ) - pDCB->SyncMode = SYNC_ENABLE; - } + dc390_updateDCB(pACB, pDCB); pDCB->SyncPeriod = 0; pDCB->SyncOffset = 0; - pDCB->NegoPeriod = (clock_period1[prom->EE_SPEED] * 25) >> 2; - - pDCB->CtrlR1 = pACB->AdaptSCSIID; - if( pDCB->DevMode & PARITY_CHK_ ) - pDCB->CtrlR1 |= PARITY_ERR_REPO; + pDCB->NegoPeriod = (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2; pDCB->CtrlR3 = FAST_CLK; - pDCB->CtrlR4 = EATER_25NS; - if( pDCB->AdpMode & ACTIVE_NEGATION) - pDCB->CtrlR4 |= NEGATE_REQACKDATA; + pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED; + if( dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) + pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK; } +/*********************************************************************** + * Function : static void dc390_updateDCB() + * + * Purpose : Set the configuration dependent DCB parameters + ***********************************************************************/ + +void dc390_updateDCB (PACB pACB, PDCB pDCB) +{ + pDCB->IdentifyMsg = IDENTIFY (pDCB->DevMode & EN_DISCONNECT_, pDCB->UnitSCSILUN); + + if (pDCB->DevMode & TAG_QUEUEING_) pDCB->SyncMode &= EN_TAG_QUEUEING | SYNC_NEGO_DONE | EN_ATN_STOP; + else pDCB->SyncMode &= SYNC_NEGO_DONE | EN_ATN_STOP; + + if( pDCB->DevMode & SYNC_NEGO_ && (!(pDCB->UnitSCSILUN) || dc390_CurrSyncOffset) ) + pDCB->SyncMode |= SYNC_ENABLE; + else + { + pDCB->SyncMode &= ~(SYNC_NEGO_DONE | SYNC_ENABLE); + pDCB->SyncOffset &= ~0x0f; + }; + + if (! (pDCB->DevMode & EN_DISCONNECT_)) pDCB->SyncMode &= ~EN_ATN_STOP; + + pDCB->CtrlR1 = pACB->pScsiHost->this_id; + if( pDCB->DevMode & PARITY_CHK_ ) + pDCB->CtrlR1 |= PARITY_ERR_REPO; +}; + + +/*********************************************************************** + * Function : static void dc390_updateDCBs () + * + * Purpose : Set the configuration dependent DCB params for all DCBs + ***********************************************************************/ + +static void dc390_updateDCBs (PACB pACB) +{ + int i; + PDCB pDCB = pACB->pLinkDCB; + for (i = 0; i < pACB->DeviceCnt; i++) + { + dc390_updateDCB (pACB, pDCB); + pDCB = pDCB->pNextDCB; + }; +}; + /*********************************************************************** - * Function : static void DC390_initSRB + * Function : static void dc390_initSRB() * * Purpose : initialize the internal structures for a given SRB * * Inputs : psrb - pointer to this scsi request block structure - * ***********************************************************************/ -void DC390_initSRB( PSRB psrb ) + +static void __inline__ dc390_initSRB( PSRB psrb ) { -#ifndef VERSION_ELF_1_2_13 -#ifdef DC390_DEBUG0 - printk("DC390 init: %08lx %08lx,",(ULONG)psrb,(ULONG)virt_to_bus(psrb)); -#endif - psrb->PhysSRB = virt_to_bus( psrb ); -#else - psrb->PhysSRB = (ULONG) psrb; -#endif + /* psrb->PhysSRB = virt_to_phys( psrb ); */ } -void DC390_linkSRB( PACB pACB ) +void dc390_linkSRB( PACB pACB ) { - USHORT count, i; - PSRB psrb; + ULONG count, i; count = pACB->SRBCount; - for( i=0; i< count; i++) { if( i != count - 1) pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1]; else pACB->SRB_array[i].pNextSRB = NULL; - psrb = (PSRB) &pACB->SRB_array[i]; - DC390_initSRB( psrb ); + dc390_initSRB( &pACB->SRB_array[i] ); } } /*********************************************************************** - * Function : static void DC390_initACB + * Function : static void dc390_initACB () * * Purpose : initialize the internal structures for a given SCSI host * * Inputs : psh - pointer to this host adapter's structure - * + * io_port, Irq, index: Resources and adapter index ***********************************************************************/ -void DC390_initACB( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) + +void __init dc390_initACB (PSH psh, ULONG io_port, UCHAR Irq, UCHAR index) { PACB pACB; - USHORT i; + UCHAR i; + DC390_AFLAGS psh->can_queue = MAX_CMD_QUEUE; psh->cmd_per_lun = MAX_CMD_PER_LUN; - psh->this_id = (int) eepromBuf[index][EE_ADAPT_SCSI_ID]; + psh->this_id = (int) dc390_eepromBuf[index][EE_ADAPT_SCSI_ID]; psh->io_port = io_port; psh->n_io_port = 0x80; psh->irq = Irq; pACB = (PACB) psh->hostdata; + DC390_LOCKA_INIT; + DC390_LOCK_ACB; + + pACB->pScsiHost = psh; + pACB->IOPortBase = (USHORT) io_port; + pACB->IRQLevel = Irq; -#ifndef VERSION_ELF_1_2_13 + DEBUG0(printk (KERN_DEBUG "DC390: Adapter index %i, ID %i, IO 0x%08x, IRQ 0x%02x\n", \ + index, psh->this_id, (int)io_port, Irq);) + psh->max_id = 8; -#ifdef CONFIG_SCSI_MULTI_LUN - if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) - psh->max_lun = 8; - else -#endif - psh->max_lun = 1; -#endif - pACB->max_id = 7; - if( pACB->max_id == eepromBuf[index][EE_ADAPT_SCSI_ID] ) - pACB->max_id--; -#ifdef CONFIG_SCSI_MULTI_LUN - if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) - pACB->max_lun = 7; - else -#endif - pACB->max_lun = 0; + if( psh->max_id - 1 == dc390_eepromBuf[index][EE_ADAPT_SCSI_ID] ) + psh->max_id--; + psh->max_lun = 1; + if( dc390_eepromBuf[index][EE_MODE2] & LUN_CHECK ) + psh->max_lun = 8; - pACB->pScsiHost = psh; - pACB->IOPortBase = (USHORT) io_port; pACB->pLinkDCB = NULL; pACB->pDCBRunRobin = NULL; pACB->pActiveDCB = NULL; @@ -1129,475 +1737,202 @@ pACB->SRBCount = MAX_SRB_CNT; pACB->AdapterIndex = index; pACB->status = 0; - pACB->AdaptSCSIID = eepromBuf[index][EE_ADAPT_SCSI_ID]; - pACB->HostID_Bit = (1 << pACB->AdaptSCSIID); - pACB->AdaptSCSILUN = 0; + psh->this_id = dc390_eepromBuf[index][EE_ADAPT_SCSI_ID]; pACB->DeviceCnt = 0; - pACB->IRQLevel = Irq; - pACB->TagMaxNum = eepromBuf[index][EE_TAG_CMD_NUM] << 2; + pACB->DCBCnt = 0; + pACB->TagMaxNum = 2 << dc390_eepromBuf[index][EE_TAG_CMD_NUM]; pACB->ACBFlag = 0; pACB->scan_devices = 1; - pACB->Gmode2 = eepromBuf[index][EE_MODE2]; - if( eepromBuf[index][EE_MODE2] & LUN_CHECK ) - pACB->LUNchk = 1; - pACB->pDCB_free = &pACB->DCB_array[0]; - DC390_linkSRB( pACB ); + pACB->Gmode2 = dc390_eepromBuf[index][EE_MODE2]; + dc390_linkSRB( pACB ); pACB->pTmpSRB = &pACB->TmpSRB; - DC390_initSRB( pACB->pTmpSRB ); + dc390_initSRB( pACB->pTmpSRB ); for(i=0; iDCBmap[i] = 0; + pACB->sel_timeout = SEL_TIMEOUT; + pACB->glitch_cfg = EATER_25NS; + pACB->Cmds = pACB->CmdInQ = pACB->CmdOutOfSRB = pACB->SelLost = 0; } /*********************************************************************** - * Function : static int DC390_initAdapter + * Function : static int dc390_initAdapter () * * Purpose : initialize the SCSI chip ctrl registers * * Inputs : psh - pointer to this host adapter's structure + * io_port, Irq, index: Resources * + * Outputs: 0 on success, -1 on error ***********************************************************************/ -int DC390_initAdapter( PSH psh, ULONG io_port, UCHAR Irq, USHORT index ) -{ - USHORT ioport; - UCHAR bval; - PACB pACB, pacb; - USHORT used_irq = 0; - pacb = pACB_start; - if( pacb != NULL ) - { - for ( ; (pacb != (PACB) -1) ; ) - { - if( pacb->IRQLevel == Irq ) - { - used_irq = 1; - break; - } - else - pacb = pacb->pNextACB; - } - } +int __init dc390_initAdapter (PSH psh, ULONG io_port, UCHAR Irq, UCHAR index) +{ + PACB pACB, pACB2; + UCHAR used_irq = 0, dstate; + int i; + + pACB = (PACB) psh->hostdata; + + for ( pACB2 = dc390_pACB_start; pACB2 ; ) + { + if( pACB2->IRQLevel == Irq ) + { + used_irq = 1; + break; + } + else + pACB2 = pACB2->pNextACB; + } - if( !used_irq ) - { -#ifdef VERSION_ELF_1_2_13 - if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT, "tmscsim")) -#else - if( request_irq(Irq, DC390_Interrupt, SA_INTERRUPT | SA_SHIRQ, "tmscsim", NULL)) -#endif + if (check_region (io_port, psh->n_io_port)) { - printk("DC390: register IRQ error!\n"); + printk(KERN_ERR "DC390: register IO ports error!\n"); return( -1 ); } - } - - request_region(io_port,psh->n_io_port,"tmscsim"); - - ioport = (USHORT) io_port; - - pACB = (PACB) psh->hostdata; - bval = SEL_TIMEOUT; /* 250ms selection timeout */ - outb(bval,ioport+Scsi_TimeOut); - - bval = CLK_FREQ_40MHZ; /* Conversion factor = 0 , 40MHz clock */ - outb(bval,ioport+Clk_Factor); - - bval = NOP_CMD; /* NOP cmd - clear command register */ - outb(bval,ioport+ScsiCmd); - - bval = EN_FEATURE+EN_SCSI2_CMD; /* Enable Feature and SCSI-2 */ - outb(bval,ioport+CtrlReg2); - - bval = FAST_CLK; /* fast clock */ - outb(bval,ioport+CtrlReg3); - - bval = EATER_25NS; - if( eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION ) - bval |= NEGATE_REQACKDATA; - outb(bval,ioport+CtrlReg4); - - bval = DIS_INT_ON_SCSI_RST; /* Disable SCSI bus reset interrupt */ - outb(bval,ioport+CtrlReg1); - - return(0); -} - - -void -DC390_EnableCfg( USHORT mechnum, UCHAR regval ) -{ - ULONG wlval; - - if(mechnum == 2) - { - outb(mech2bus, PCI_CFG2_FORWARD_REG); - outb(mech2CfgSPenR, PCI_CFG2_ENABLE_REG); - } - else - { - regval &= 0xFC; - wlval = mech1addr; - wlval |= (((ULONG)regval) & 0xff); - outl(wlval, PCI_CFG1_ADDRESS_REG); - } -} - - -void -DC390_DisableCfg( USHORT mechnum ) -{ - - if(mechnum == 2) - outb(0, PCI_CFG2_ENABLE_REG); - else - outl(0, PCI_CFG1_ADDRESS_REG); -} - - -UCHAR -DC390_inByte( USHORT mechnum, UCHAR regval ) -{ - UCHAR bval; - ULONG wval; - ULONG flags; - - save_flags(flags); - cli(); - DC390_EnableCfg( mechnum, regval ); - if(mechnum == 2) - { - wval = mech2Agent; - wval <<= 8; - wval |= ((USHORT) regval) & 0xff; - bval = inb(wval); - } - else - { - regval &= 3; - bval = inb(PCI_CFG1_DATA_REG | regval); - } - DC390_DisableCfg(mechnum); - restore_flags(flags); - return(bval); -} - - -USHORT -DC390_inWord( USHORT mechnum, UCHAR regval ) -{ - USHORT wval; - ULONG flags; - - save_flags(flags); - cli(); - DC390_EnableCfg(mechnum,regval); - if(mechnum == 2) - { - wval = mech2Agent; - wval <<= 8; - wval |= regval; - wval = inw(wval); - } - else - { - regval &= 3; - wval = inw(PCI_CFG1_DATA_REG | regval); - } - DC390_DisableCfg(mechnum); - restore_flags(flags); - return(wval); -} - - -ULONG -DC390_inDword(USHORT mechnum, UCHAR regval ) -{ - ULONG wlval; - ULONG flags; - USHORT wval; - - save_flags(flags); - cli(); - DC390_EnableCfg(mechnum,regval); - if(mechnum == 2) - { - wval = mech2Agent; - wval <<= 8; - wval |= regval; - wlval = inl(wval); - } - else - { - wlval = inl(PCI_CFG1_DATA_REG); - } - DC390_DisableCfg(mechnum); - restore_flags(flags); - return(wlval); -} - - -void -DC390_OutB(USHORT mechnum, UCHAR regval, UCHAR bval ) -{ - - USHORT wval; - ULONG flags; - - save_flags(flags); - cli(); - DC390_EnableCfg(mechnum,regval); - if(mechnum == 2) - { - wval = mech2Agent; - wval <<= 8; - wval |= regval; - outb(bval, wval); - } - else - { - regval &= 3; - outb(bval, PCI_CFG1_DATA_REG | regval); - } - DC390_DisableCfg(mechnum); - restore_flags(flags); -} - - -void -DC390_EnDisableCE( UCHAR mode, USHORT mechnum, PUCHAR regval ) -{ - - UCHAR bval; - - bval = 0; - if(mode == ENABLE_CE) - *regval = 0xc0; - else - *regval = 0x80; - DC390_OutB(mechnum,*regval,bval); - if(mode == DISABLE_CE) - DC390_OutB(mechnum,*regval,bval); - udelay(160); -} - - -void -DC390_EEpromOutDI( USHORT mechnum, PUCHAR regval, USHORT Carry ) -{ - UCHAR bval; - - bval = 0; - if(Carry) - { - bval = 0x40; - *regval = 0x80; - DC390_OutB(mechnum,*regval,bval); - } - udelay(160); - bval |= 0x80; - DC390_OutB(mechnum,*regval,bval); - udelay(160); - bval = 0; - DC390_OutB(mechnum,*regval,bval); - udelay(160); -} - - -UCHAR -DC390_EEpromInDO( USHORT mechnum ) -{ - UCHAR bval,regval; - - regval = 0x80; - bval = 0x80; - DC390_OutB(mechnum,regval,bval); - udelay(160); - bval = 0x40; - DC390_OutB(mechnum,regval,bval); - udelay(160); - regval = 0x0; - bval = DC390_inByte(mechnum,regval); - if(bval == 0x22) - return(1); else - return(0); -} - - -USHORT -EEpromGetData1( USHORT mechnum ) -{ - UCHAR i; - UCHAR carryFlag; - USHORT wval; - - wval = 0; - for(i=0; i<16; i++) - { - wval <<= 1; - carryFlag = DC390_EEpromInDO(mechnum); - wval |= carryFlag; - } - return(wval); -} - - -void -DC390_Prepare( USHORT mechnum, PUCHAR regval, UCHAR EEpromCmd ) -{ - UCHAR i,j; - USHORT carryFlag; - - carryFlag = 1; - j = 0x80; - for(i=0; i<9; i++) - { - DC390_EEpromOutDI(mechnum,regval,carryFlag); - carryFlag = (EEpromCmd & j) ? 1 : 0; - j >>= 1; - } -} - + request_region (io_port, psh->n_io_port, "tmscsim"); -void -DC390_ReadEEprom( USHORT mechnum, USHORT index ) -{ - UCHAR regval,cmd; - PUSHORT ptr; - USHORT i; + DC390_read8_ (INT_Status, io_port); /* Reset Pending INT */ - ptr = (PUSHORT) &eepromBuf[index][0]; - cmd = EEPROM_READ; - for(i=0; i<0x40; i++) + if( !used_irq ) { - DC390_EnDisableCE(ENABLE_CE, mechnum, ®val); - DC390_Prepare(mechnum, ®val, cmd); - *ptr = EEpromGetData1(mechnum); - ptr++; - cmd++; - DC390_EnDisableCE(DISABLE_CE,mechnum,®val); + if( (i = request_irq(Irq, do_DC390_Interrupt, DC390_IRQ, "tmscsim", NULL) )) + { + printk(KERN_ERR "DC390: register IRQ error!\n"); + return( -1 ); + } } -} - -USHORT -DC390_CheckEEpromCheckSum( USHORT MechNum, USHORT index ) -{ - USHORT wval, rc, *ptr; - UCHAR i; - - DC390_ReadEEprom( MechNum, index ); - wval = 0; - ptr = (PUSHORT) &eepromBuf[index][0]; - for(i=0; i<128 ;i+=2, ptr++) - wval += *ptr; - if( wval == 0x1234 ) - rc = 0; + if( !dc390_pACB_start ) + { + pACB2 = NULL; + dc390_pACB_start = pACB; + dc390_pACB_current = pACB; + pACB->pNextACB = NULL; + } else - rc = -1; - return( rc ); -} - - -USHORT -DC390_ToMech( USHORT Mechnum, USHORT BusDevFunNum ) -{ - USHORT devnum; - - devnum = BusDevFunNum; + { + pACB2 = dc390_pACB_current; + dc390_pACB_current->pNextACB = pACB; + dc390_pACB_current = pACB; + pACB->pNextACB = NULL; + }; + + DC390_write8 (CtrlReg1, DIS_INT_ON_SCSI_RST | psh->this_id); /* Disable SCSI bus reset interrupt */ + + if (pACB->Gmode2 & RST_SCSI_BUS) + { + dc390_ResetSCSIBus( pACB ); + /* Unlock before ? */ + for( i=0; i<600; i++ ) + udelay(1000); + }; + pACB->ACBFlag = 0; + DC390_read8 (INT_Status); /* Reset Pending INT */ + + DC390_write8 (Scsi_TimeOut, SEL_TIMEOUT); /* 250ms selection timeout */ + DC390_write8 (Clk_Factor, CLK_FREQ_40MHZ); /* Conversion factor = 0 , 40MHz clock */ + DC390_write8 (ScsiCmd, NOP_CMD); /* NOP cmd - clear command register */ + DC390_write8 (CtrlReg2, EN_FEATURE+EN_SCSI2_CMD); /* Enable Feature and SCSI-2 */ + DC390_write8 (CtrlReg3, FAST_CLK); /* fast clock */ + DC390_write8 (CtrlReg4, pACB->glitch_cfg | /* glitch eater */ + (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ? NEGATE_REQACKDATA : 0); /* Negation */ + DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); + DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); + DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT); + dstate = DC390_read8 (DMA_Status); + DC390_write8 (DMA_Status, dstate); /* clear */ - if(Mechnum == 2) - { - if(devnum & 0x80) - return(-1); - mech2bus = (UCHAR)((devnum & 0xff00) >> 8); /* Bus num */ - mech2Agent = ((UCHAR)(devnum & 0xff)) >> 3; /* Dev num */ - mech2Agent |= 0xc0; - mech2CfgSPenR = ((UCHAR)(devnum & 0xff)) & 0x07; /* Fun num */ - mech2CfgSPenR = (mech2CfgSPenR << 1) | 0x20; - } - else /* use mech #1 method */ - { - mech1addr = 0x80000000 | ((ULONG)devnum << 8); - } return(0); } + /*********************************************************************** - * Function : static int DC390_init (struct Scsi_Host *host) + * Function : static int DC390_init (struct Scsi_Host *host, ...) * * Purpose : initialize the internal structures for a given SCSI host * - * Inputs : host - pointer to this host adapter's structure/ + * Inputs : host - pointer to this host adapter's structure + * io_port - IO ports mapped to this adapter + * Irq - IRQ assigned to this adpater + * PDEVDECL - PCI access handle + * index - Adapter index * - * Preconditions : when this function is called, the chip_type - * field of the pACB structure MUST have been set. + * Outputs: 0 on success, -1 on error + * + * Note: written in capitals, because the locking is only done here, + * not in DC390_detect, called from outside ***********************************************************************/ -static int -DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, USHORT index, USHORT MechNum) +static int __init DC390_init (PSHT psht, ULONG io_port, UCHAR Irq, PDEVDECL, UCHAR index) { PSH psh; PACB pACB; - - if( !DC390_CheckEEpromCheckSum( MechNum, index) ) + DC390_AFLAGS + + if (dc390_CheckEEpromCheckSum (PDEV, index)) { - psh = scsi_register( psht, sizeof(DC390_ACB) ); - if( !psh ) - return( -1 ); - if( !pSH_start ) - { - pSH_start = psh; - pSH_current = psh; - } - else - { - pSH_current->next = psh; - pSH_current = psh; - } +#ifdef CONFIG_SCSI_DC390T_NOGENSUPP + printk (KERN_ERR "DC390_init: No EEPROM found!\n"); + return( -1 ); +#else + int period; + printk (KERN_INFO "DC390_init: No EEPROM found!\n"); + printk (KERN_INFO "DC390_init: Trying default EEPROM settings:\n"); + dc390_checkparams (); + period = dc390_clock_period1[tmscsim[1]]; + printk (KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz)," + " DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i)\n", tmscsim[0], tmscsim[1], + 40 / period, ((40%period)*10 + period/2) / period, + (UCHAR)tmscsim[2], (UCHAR)tmscsim[3], tmscsim[4], 2 << (tmscsim[4])); + dc390_EEpromDefaults (index); +#endif + }; + + psh = scsi_register( psht, sizeof(DC390_ACB) ); + if( !psh ) return( -1 ); + + pACB = (PACB) psh->hostdata; + DC390_LOCKA_INIT; + DC390_LOCK_ACB; -#ifdef DC390_DEBUG0 - printk("DC390: pSH = %8x,", (UINT) psh); - printk("DC390: Index %02i,", index); +#if 0 + if( !dc390_pSH_start ) + { + dc390_pSH_start = psh; + dc390_pSH_current = psh; + } + else + { + dc390_pSH_current->next = psh; + dc390_pSH_current = psh; + } #endif - DC390_initACB( psh, io_port, Irq, index ); - if( !DC390_initAdapter( psh, io_port, Irq, index ) ) - { - pACB = (PACB) psh->hostdata; - if( !pACB_start ) - { - pACB_start = pACB; - pACB_current = pACB; - pACB->pNextACB = (PACB) -1; - } - else - { - pACB_current->pNextACB = pACB; - pACB_current = pACB; - pACB->pNextACB = (PACB) -1; - } + DEBUG0(printk("DC390: pSH = %8x,", (UINT) psh);) + DEBUG0(printk("DC390: Index %02i,", index);) -#ifdef DC390_DEBUG0 - printk("DC390: pACB = %8x, pDCB_array = %8x, pSRB_array = %8x\n", - (UINT) pACB, (UINT) pACB->DCB_array, (UINT) pACB->SRB_array); - printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n", - sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) ); -#endif + dc390_initACB( psh, io_port, Irq, index ); + pACB = (PACB) psh->hostdata; + + PDEVSET; - } - else - { - pSH_start = NULL; - scsi_unregister( psh ); - return( -1 ); - } - return( 0 ); + if( !dc390_initAdapter( psh, io_port, Irq, index ) ) + { + DEBUG0(printk("DC390: pACB = %8x, pDCBmap = %8x, pSRB_array = %8x\n",\ + (UINT) pACB, (UINT) pACB->DCBmap, (UINT) pACB->SRB_array);) + DEBUG0(printk("DC390: ACB size= %4x, DCB size= %4x, SRB size= %4x\n",\ + sizeof(DC390_ACB), sizeof(DC390_DCB), sizeof(DC390_SRB) );) + + DC390_UNLOCK_ACB; + return (0); } else { - printk("DC390_init: EEPROM reading error!\n"); + //dc390_pSH_start = NULL; + scsi_unregister( psh ); + DC390_UNLOCK_ACB; return( -1 ); } } @@ -1616,136 +1951,443 @@ * ***********************************************************************/ -int -DC390_detect(Scsi_Host_Template *psht) -{ -#ifdef FOR_PCI_OK - UCHAR pci_bus, pci_device_fn; - int error = 0; - USHORT chipType = 0; - USHORT i; -#endif +#ifndef NEW_PCI +/* Acc. to PCI 2.1 spec it's up to the driver to enable Bus mastering: + * We use pci_set_master () for 2.1.x and this func for 2.0.x: */ +static void __init dc390_set_master (PDEVDECL) +{ + USHORT cmd; + UCHAR lat; + + PCI_READ_CONFIG_WORD (PDEV, PCI_COMMAND, &cmd); + + if (! (cmd & PCI_COMMAND_MASTER)) { + printk("PCI: Enabling bus mastering for device %02x:%02x\n", + PCI_BUS_DEV); + cmd |= PCI_COMMAND_MASTER; + PCI_WRITE_CONFIG_WORD(PDEV, PCI_COMMAND, cmd); + } + PCI_READ_CONFIG_BYTE (PDEV, PCI_LATENCY_TIMER, &lat); + if (lat < 16 /* || lat == 255 */) { + printk("PCI: Setting latency timer of device %02x:%02x from %i to 64\n", + PCI_BUS_DEV, lat); + PCI_WRITE_CONFIG_BYTE(PDEV, PCI_LATENCY_TIMER, 64); + } + +}; +#endif /* ! NEW_PCI */ + +static void __init dc390_set_pci_cfg (PDEVDECL) +{ + USHORT cmd; + PCI_READ_CONFIG_WORD (PDEV, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO; + PCI_WRITE_CONFIG_WORD (PDEV, PCI_COMMAND, cmd); + PCI_WRITE_CONFIG_WORD (PDEV, PCI_STATUS, (PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY)); +}; + +int __init DC390_detect (Scsi_Host_Template *psht) +{ + PDEVDECL0; UCHAR irq; - UCHAR istatus; -#ifndef VERSION_ELF_1_2_13 UINT io_port; -#else - ULONG io_port; -#endif - USHORT adaptCnt = 0; /* Number of boards detected */ - USHORT pci_index = 0; /* Device index to PCI BIOS calls */ - USHORT MechNum, BusDevFunNum; - ULONG wlval; - -#ifndef VERSION_ELF_1_2_13 - psht->proc_dir = &proc_scsi_tmscsim; -#endif + DC390_IFLAGS DC390_DFLAGS - InitialTime = 1; - pSHT_start = psht; - pACB_start = NULL; + DC390_LOCK_DRV; + //dc390_pSHT_start = psht; + dc390_pACB_start = NULL; - MechNum = 1; - for( ; (MechNum < 3) && (!adaptCnt); MechNum++) - { - BusDevFunNum = 0; - for (; adaptCnt < MAX_ADAPTER_NUM ;) + if ( PCI_PRESENT ) + while (PCI_FIND_DEVICE (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974)) { - if( !DC390_ToMech( MechNum, BusDevFunNum) ) - { - wlval = DC390_inDword( MechNum, PCI_VENDOR_ID); - if(wlval == ( (PCI_DEVICE_ID_AMD53C974 << 16)+ - PCI_VENDOR_ID_AMD) ) - { - io_port =DC390_inDword(MechNum,PCI_BASE_ADDRESS_0) & 0xFFFE; - irq = DC390_inByte( MechNum, PCI_INTERRUPT_LINE); -#ifdef DC390_DEBUG0 - printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq); -#endif - if( !DC390_init(psht, io_port, irq, pci_index, MechNum) ) - { - adaptCnt++; - pci_index++; - istatus = inb( (USHORT)io_port+INT_Status ); /* Reset Pending INT */ -#ifdef DC390_DEBUG0 - printk("DC390: Mech=%2x,\n",(UCHAR) MechNum); -#endif - } - } - } - if( BusDevFunNum != 0xfff8 ) - BusDevFunNum += 8; /* next device # */ - else - break; - } - } + DC390_LOCK_IO; /* Remove this when going to new eh */ + PCI_GET_IO_AND_IRQ; + DEBUG0(printk(KERN_DEBUG "DC390(%i): IO_PORT=%04x,IRQ=%x\n", dc390_adapterCnt, (UINT) io_port, irq);) -#ifdef FOR_PCI_OK - if ( pcibios_present() ) - { - for (i = 0; i < MAX_ADAPTER_NUM; ++i) - { - if( !pcibios_find_device( PCI_VENDOR_ID_AMD, - PCI_DEVICE_ID_AMD53C974, - pci_index, &pci_bus, &pci_device_fn) ) + if( !DC390_init(psht, io_port, irq, PDEV, dc390_adapterCnt)) { - chipType = PCI_DEVICE_ID_AMD53C974; - pci_index++; - } - - if( chipType ) - { - - error = pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &io_port); - error |= pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &irq); - if( error ) - { - printk("DC390_detect: reading configuration registers error!\n"); - InitialTime = 0; - return( 0 ); - } - - (USHORT) io_port = (USHORT) io_port & 0xFFFE; -#ifdef DC390_DEBUG0 - printk("DC390: IO_PORT=%4x,IRQ=%x,\n",(UINT) io_port, irq); -#endif - if( !DC390_init(psht, io_port, irq, i) ) - adaptCnt++; - chipType = 0; - } - else - break; + PCI_SET_MASTER; + dc390_set_pci_cfg (PDEV); + dc390_adapterCnt++; + }; + DC390_UNLOCK_IO; /* Remove when going to new eh */ } - } -#endif - - InitialTime = 0; - adapterCnt = adaptCnt; - return( adaptCnt ); + else + printk (KERN_ERR "DC390: No PCI BIOS found!\n"); + + if (dc390_adapterCnt) + psht->proc_dir = &DC390_proc_scsi_tmscsim; + + printk(KERN_INFO "DC390: %i adapters found\n", dc390_adapterCnt); + DC390_UNLOCK_DRV; + return( dc390_adapterCnt ); } -#ifndef VERSION_ELF_1_2_13 +/*********************************************************************** + * Functions: dc390_inquiry(), dc390_inquiry_done() + * + * Purpose: When changing speed etc., we have to issue an INQUIRY + * command to make sure, we agree upon the nego parameters + * with the device + ***********************************************************************/ + +static void dc390_inquiry_done (Scsi_Cmnd* cmd) +{ + printk (KERN_INFO "DC390: INQUIRY (ID %02x LUN %02x) returned %08x\n", + cmd->target, cmd->lun, cmd->result); + if (cmd->result) + { + PACB pACB = (PACB)cmd->host->hostdata; + PDCB pDCB = dc390_findDCB (pACB, cmd); + printk ("DC390: Unsetting DsCn, Sync and TagQ!\n"); + if (pDCB) + { + pDCB->DevMode &= ~(SYNC_NEGO_ | TAG_QUEUEING_ | EN_DISCONNECT_ ); + dc390_updateDCB (pACB, pDCB); + }; + }; + kfree (cmd->buffer); + kfree (cmd); +}; + +void dc390_inquiry (PACB pACB, PDCB pDCB) +{ + char* buffer; + Scsi_Cmnd* cmd; + buffer = kmalloc (256, GFP_ATOMIC); + cmd = kmalloc (sizeof (Scsi_Cmnd), GFP_ATOMIC); + + memset (buffer, 0, 256); + memset (cmd, 0, sizeof(Scsi_Cmnd)); + cmd->cmnd[0] = INQUIRY; + cmd->cmnd[1] = (pDCB->UnitSCSILUN << 5) & 0xe0; + cmd->cmnd[4] = 0xff; + + cmd->cmd_len = 6; cmd->old_cmd_len = 6; + cmd->host = pACB->pScsiHost; + cmd->target = pDCB->UnitSCSIID; + cmd->lun = pDCB->UnitSCSILUN; + cmd->serial_number = 1; + cmd->bufflen = 128; + cmd->buffer = buffer; + cmd->request_bufflen = 128; + cmd->request_buffer = &buffer[128]; + cmd->done = dc390_inquiry_done; + cmd->scsi_done = dc390_inquiry_done; + cmd->timeout_per_command = HZ; + + cmd->request.rq_status = RQ_SCSI_BUSY; + + printk (KERN_INFO "DC390: Queue INQUIRY command to dev ID %02x LUN %02x\n", + pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + DC390_queue_command (cmd, dc390_inquiry_done); +}; /******************************************************************** - * Function: tmscsim_set_info() + * Function: dc390_set_info() * - * Purpose: Set adapter info (!) - * - * Not yet implemented + * Purpose: Change adapter config * + * Strings are parsed similar to the output of tmscsim_proc_info () + * '-' means no change *******************************************************************/ -int tmscsim_set_info(char *buffer, int length, struct Scsi_Host *shpnt) +static int dc390_scanf (char** p1, char** p2, int* var) { - return(-ENOSYS); /* Currently this is a no-op */ -} + *p2 = *p1; + *var = simple_strtoul (*p2, p1, 10); + if (*p2 == *p1) return -1; + *p1 = strtok (0, " \t\n:=,;."); + return 0; +}; + +#define SCANF(p1, p2, var, min, max) \ +if (dc390_scanf (&p1, &p2, &var)) goto einv; \ +else if (varmax) goto einv2 + +static int dc390_yesno (char** p, char* var, char bmask) +{ + switch (**p) + { + case 'Y': *var |= bmask; break; + case 'N': *var &= ~bmask; break; + case '-': break; + default: return -1; + } + *p = strtok (0, " \t\n:=,;"); + return 0; +}; + +#define YESNO(p, var, bmask) \ +if (dc390_yesno (&p, &var, bmask)) goto einv; \ +else dc390_updateDCB (pACB, pDCB); \ +if (!p) goto ok + +static int dc390_search (char **p1, char **p2, char *var, char* txt, int max, int scale, char* ign) +{ + int dum; + if (! memcmp (*p1, txt, strlen(txt))) + { + *p2 = strtok (0, " \t\n:=,;"); + if (!*p2) return -1; + dum = simple_strtoul (*p2, p1, 10); + if (*p2 == *p1) return -1; + if (dum >= 0 && dum <= max) + { *var = (dum * 100) / scale; } + else return -2; + *p1 = strtok (0, " \t\n:=,;"); + if (*ign && *p1 && strlen(*p1) >= strlen(ign) && + !(memcmp (*p1, ign, strlen(ign)))) + *p1 = strtok (0, " \t\n:=,;"); + + } + return 0; +}; + +#define SEARCH(p1, p2, var, txt, max) \ +if (dc390_search (&p1, &p2, (PUCHAR)(&var), txt, max, 100, "")) goto einv2; \ +else if (!p1) goto ok2 + +#define SEARCH2(p1, p2, var, txt, max, scale) \ +if (dc390_search (&p1, &p2, &var, txt, max, scale, "")) goto einv2; \ +else if (!p1) goto ok2 + +#define SEARCH3(p1, p2, var, txt, max, scale, ign) \ +if (dc390_search (&p1, &p2, &var, txt, max, scale, ign)) goto einv2; \ +else if (!p1) goto ok2 + + +#ifdef DC390_PARSEDEBUG +static char _prstr[256]; +char* prstr (char* p, char* e) +{ + char* c = _prstr; + while (p < e) + if (*p == 0) { *c++ = ':'; p++; } + else if (*p == 10) { *c++ = '\\'; *c++ = 'n'; p++; } + else *c++ = *p++; + *c = 0; + return _prstr; +}; +#endif + +int dc390_set_info (char *buffer, int length, PACB pACB) +{ + char *pos = buffer, *p0 = buffer; + char needs_inquiry = 0; + int dum = 0; + char dev; + PDCB pDCB = pACB->pLinkDCB; + DC390_IFLAGS + DC390_AFLAGS + pos[length] = 0; + + DC390_LOCK_IO; + DC390_LOCK_ACB; + /* UPPERCASE */ + /* Don't use kernel toupper, because of 2.0.x bug: ctmp unexported */ + while (*pos) + { if (*pos >='a' && *pos <= 'z') *pos = *pos + 'A' - 'a'; pos++; }; + + /* We should protect __strtok ! */ + /* spin_lock (strtok_lock); */ + + /* Remove WS */ + pos = strtok (buffer, " \t:\n=,;"); + if (!pos) goto ok; + + next: + if (!memcmp (pos, "RESET", 5)) goto reset; + else if (!memcmp (pos, "INQUIRY", 7)) goto inquiry; + else if (!memcmp (pos, "REMOVE", 6)) goto remove; + + if (isdigit (*pos)) + { + /* Device config line */ + int dev, id, lun; char* pdec; + char olddevmode; + + SCANF (pos, p0, dev, 0, pACB->DCBCnt-1); + if (pos) { SCANF (pos, p0, id, 0, 7); } else goto einv; + if (pos) { SCANF (pos, p0, lun, 0, 7); } else goto einv; + if (!pos) goto einv; + + PARSEDEBUG(printk (KERN_INFO "DC390: config line %i %i %i:\"%s\"\n", dev, id, lun, prstr (pos, &buffer[length]));) + pDCB = pACB->pLinkDCB; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + /* Sanity Check */ + if (pDCB->UnitSCSIID != id || pDCB->UnitSCSILUN != lun) + { + printk (KERN_ERR "DC390: no such device: Idx=%02i ID=%02i LUN=%02i\n", + dev, id, lun); + goto einv2; + }; + + olddevmode = pDCB->DevMode; + YESNO (pos, pDCB->DevMode, PARITY_CHK_); + needs_inquiry++; + YESNO (pos, pDCB->DevMode, SYNC_NEGO_); + if ((olddevmode & SYNC_NEGO_) == (pDCB->DevMode & SYNC_NEGO_)) needs_inquiry--; + needs_inquiry++; + YESNO (pos, pDCB->DevMode, EN_DISCONNECT_); + if ((olddevmode & EN_DISCONNECT_) == (pDCB->DevMode & EN_DISCONNECT_)) needs_inquiry--; + YESNO (pos, pDCB->DevMode, SEND_START_); + needs_inquiry++; + YESNO (pos, pDCB->DevMode, TAG_QUEUEING_); + if ((olddevmode & TAG_QUEUEING_) == (pDCB->DevMode & TAG_QUEUEING_)) needs_inquiry--; + YESNO (pos, pDCB->SyncMode, EN_ATN_STOP); + + dc390_updateDCB (pACB, pDCB); + if (!pos) goto ok; + + olddevmode = pDCB->NegoPeriod; + /* Look for decimal point (Speed) */ + pdec = pos; + while (pdec++ < &buffer[length]) if (*pdec == '.') break; + /* NegoPeriod */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 76, 800); + pDCB->NegoPeriod = dum >> 2; + if (pDCB->NegoPeriod != olddevmode) needs_inquiry++; + if (!pos) goto ok; + if (memcmp (pos, "NS", 2) == 0) pos = strtok (0, " \t\n:=,;."); + } + else pos = strtok (0, " \t\n:=,;."); + if (!pos) goto ok; + + /* Speed: NegoPeriod */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 1, 13); + pDCB->NegoPeriod = (1000/dum) >> 2; + if (pDCB->NegoPeriod != olddevmode && !pos) needs_inquiry++; + if (!pos) goto ok; + /* decimal */ + if (pos-1 == pdec) + { + int dumold = dum; + dum = simple_strtoul (pos, &p0, 10) * 10; + for (; p0-pos > 1; p0--) dum /= 10; + pDCB->NegoPeriod = (100000/(100*dumold + dum)) >> 2; + if (pDCB->NegoPeriod < 19) pDCB->NegoPeriod = 19; + if (pDCB->NegoPeriod != olddevmode) needs_inquiry++; + pos = strtok (0, " \t\n:=,;"); + if (!pos) goto ok; + }; + if (*pos == 'M') pos = strtok (0, " \t\n:=,;"); + } + else pos = strtok (0, " \t\n:=,;"); + /* dc390_updateDCB (pACB, pDCB); */ + if (!pos) goto ok; + + olddevmode = pDCB->SyncOffset; + /* SyncOffs */ + if (*pos != '-') + { + SCANF (pos, p0, dum, 0, 0x0f); + pDCB->SyncOffset = dum; + if (pDCB->SyncOffset > olddevmode) needs_inquiry++; + } + else pos = strtok (0, " \t\n:=,;"); + dc390_updateDCB (pACB, pDCB); + } + else + { + char* p1 = pos; UCHAR dum; + PARSEDEBUG(printk (KERN_INFO "DC390: chg adapt cfg \"%s\"\n", prstr (pos, &buffer[length]));) + dum = GLITCH_TO_NS (pACB->glitch_cfg); + /* Adapter setting */ + SEARCH (pos, p0, pACB->pScsiHost->max_id, "MAXID", 8); + SEARCH (pos, p0, pACB->pScsiHost->max_lun, "MAXLUN", 8); + SEARCH (pos, p0, pACB->pScsiHost->this_id, "ADAPTERID", 7); + SEARCH (pos, p0, pACB->TagMaxNum, "TAGMAXNUM", 32); + SEARCH (pos, p0, pACB->ACBFlag, "ACBFLAG", 255); + SEARCH3 (pos, p0, dum, "GLITCHEATER", 40, 1000, "NS"); + SEARCH3 (pos, p0, pACB->sel_timeout, "SELTIMEOUT", 400, 163, "MS"); + ok2: + pACB->glitch_cfg = NS_TO_GLITCH (dum); + if (pACB->sel_timeout < 60) pACB->sel_timeout = 60; + dum = 0; while (1 << dum <= pACB->TagMaxNum) dum ++; + pACB->TagMaxNum &= (1 << --dum); + if (pos == p1) goto einv; + dc390_updateDCBs (pACB); + } + if (pos) goto next; + + ok: + /* spin_unlock (strtok_lock); */ + DC390_UNLOCK_ACB; + if (needs_inquiry) + { dc390_updateDCB (pACB, pDCB); dc390_inquiry (pACB, pDCB); }; + DC390_UNLOCK_IO; + return (length); + + einv2: + pos = p0; + einv: + /* spin_unlock (strtok_lock); */ + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; + printk (KERN_WARNING "DC390: parse error near \"%s\"\n", (pos? pos: "NULL")); + return (-EINVAL); + + reset: + { + Scsi_Cmnd cmd; cmd.host = pACB->pScsiHost; + printk (KERN_WARNING "DC390: Driver reset requested!\n"); + DC390_UNLOCK_ACB; + DC390_reset (&cmd, 0); + DC390_UNLOCK_IO; + }; + return (length); + + inquiry: + { + pos = strtok (0, " \t\n.:;="); if (!pos) goto einv; + dev = simple_strtoul (pos, &p0, 10); + if (dev >= pACB->DCBCnt) goto einv_dev; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + printk (KERN_NOTICE " DC390: Issue INQUIRY command to Dev(Idx) %i SCSI ID %i LUN %i\n", + dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + DC390_UNLOCK_ACB; + dc390_inquiry (pACB, pDCB); + DC390_UNLOCK_IO; + }; + return (length); + + remove: + { + pos = strtok (0, " \t\n.:;="); if (!pos) goto einv; + dev = simple_strtoul (pos, &p0, 10); + if (dev >= pACB->DCBCnt) goto einv_dev; + for (dum = 0; dum < dev; dum++) pDCB = pDCB->pNextDCB; + printk (KERN_NOTICE " DC390: Remove DCB for Dev(Idx) %i SCSI ID %i LUN %i\n", + dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + dc390_remove_dev (pACB, pDCB); + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; + }; + return (length); + + einv_dev: + printk (KERN_WARNING "DC390: Ignore cmnd to illegal Dev(Idx) %i. Valid range: 0 - %i.\n", + dev, pACB->DCBCnt - 1); + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; + return (-EINVAL); + + +} + +#undef SEARCH +#undef YESNO +#undef SCANF /******************************************************************** - * Function: tmscsim_proc_info(char* buffer, char **start, + * Function: DC390_proc_info(char* buffer, char **start, * off_t offset, int length, int hostno, int inout) * * Purpose: return SCSI Adapter/Device Info @@ -1763,84 +2405,89 @@ * ********************************************************************/ -/* KG: proc_info taken from driver aha152x.c */ - #undef SPRINTF #define SPRINTF(args...) pos += sprintf(pos, ## args) -#define YESNO(YN)\ -if (YN) SPRINTF(" Yes ");\ -else SPRINTF(" No ") +#define YESNO(YN) \ + if (YN) SPRINTF(" Yes "); \ + else SPRINTF(" No ") + -int tmscsim_proc_info(char *buffer, char **start, - off_t offset, int length, int hostno, int inout) +int DC390_proc_info (char *buffer, char **start, + off_t offset, int length, int hostno, int inout) { int dev, spd, spd1; char *pos = buffer; PSH shpnt; - PACB acbpnt; - PDCB dcbpnt; - unsigned long flags; -/* Scsi_Cmnd *ptr; */ + PACB pACB; + PDCB pDCB; + DC390_AFLAGS - acbpnt = pACB_start; + pACB = dc390_pACB_start; - while(acbpnt != (PACB)-1) + while(pACB != (PACB)-1) { - shpnt = acbpnt->pScsiHost; + shpnt = pACB->pScsiHost; if (shpnt->host_no == hostno) break; - acbpnt = acbpnt->pNextACB; + pACB = pACB->pNextACB; } - if (acbpnt == (PACB)-1) return(-ESRCH); + if (pACB == (PACB)-1) return(-ESRCH); if(!shpnt) return(-ESRCH); if(inout) /* Has data been written to the file ? */ - return(tmscsim_set_info(buffer, length, shpnt)); + return dc390_set_info(buffer, length, pACB); + + SPRINTF("Tekram DC390/AM53C974 PCI SCSI Host Adapter, "); + SPRINTF("Driver Version %s\n", DC390_VERSION); - SPRINTF("Tekram DC390(T) PCI SCSI Host Adadpter, "); - SPRINTF("Driver Version 1.10, 1996/12/05\n"); - - save_flags(flags); - cli(); + DC390_LOCK_ACB; SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); - SPRINTF("DC390 Adapter Nr %i\n", acbpnt->AdapterIndex); - SPRINTF("IOPortBase 0x%04x, ", acbpnt->IOPortBase); - SPRINTF("IRQLevel 0x%02x\n", acbpnt->IRQLevel); - - SPRINTF("MaxID %i, MaxLUN %i, ",acbpnt->max_id, acbpnt->max_lun); - SPRINTF("AdapterID %i, AdapterLUN %i\n", acbpnt->AdaptSCSIID, acbpnt->AdaptSCSILUN); - - SPRINTF("TagMaxNum %i, Status %i\n", acbpnt->TagMaxNum, acbpnt->status); - - SPRINTF("Nr of attached devices: %i\n", acbpnt->DeviceCnt); + SPRINTF("%s Adapter Nr %i\n", dc390_adapname, pACB->AdapterIndex); + SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase); + SPRINTF("IRQLevel 0x%02x\n", pACB->IRQLevel); + + SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun); + SPRINTF("AdapterID %i, SelTimeout %i ms\n", + shpnt->this_id, (pACB->sel_timeout*164)/100); + + SPRINTF("TagMaxNum %i, Status %i, ACBFlag %i, GlitchEater %i ns\n", + pACB->TagMaxNum, pACB->status, pACB->ACBFlag, GLITCH_TO_NS(pACB->glitch_cfg)*12); + + SPRINTF("Statistics: Cmnds %li, Cmnds not sent directly %li, Out of SRB conds %li\n", + pACB->Cmds, pACB->CmdInQ, pACB->CmdOutOfSRB); + SPRINTF(" Lost arbitrations %li\n", pACB->SelLost); + + SPRINTF("Nr of attached devices: %i, Nr of DCBs: %i\n", pACB->DeviceCnt, pACB->DCBCnt); - SPRINTF("Un ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs\n"); + SPRINTF("Idx ID LUN Prty Sync DsCn SndS TagQ STOP NegoPeriod SyncSpeed SyncOffs\n"); - dcbpnt = acbpnt->pLinkDCB; - for (dev = 0; dev < acbpnt->DeviceCnt; dev++) + pDCB = pACB->pLinkDCB; + for (dev = 0; dev < pACB->DCBCnt; dev++) { - SPRINTF("%02i %02i %02i ", dev, dcbpnt->UnitSCSIID, dcbpnt->UnitSCSILUN); - YESNO(dcbpnt->DevMode & PARITY_CHK_); - YESNO(dcbpnt->SyncMode & SYNC_NEGO_DONE); - YESNO(dcbpnt->DevMode & EN_DISCONNECT_); - YESNO(dcbpnt->DevMode & SEND_START_); - YESNO(dcbpnt->SyncMode & EN_TAG_QUEUING); - SPRINTF(" %03i ns ", (dcbpnt->NegoPeriod) << 2); - if (dcbpnt->SyncOffset & 0x0f) + SPRINTF("%02i %02i %02i ", dev, pDCB->UnitSCSIID, pDCB->UnitSCSILUN); + YESNO(pDCB->DevMode & PARITY_CHK_); + YESNO(pDCB->SyncMode & SYNC_NEGO_DONE); + YESNO(pDCB->DevMode & EN_DISCONNECT_); + //YESNO(pDCB->SyncMode & EN_ATN_STOP); + YESNO(pDCB->DevMode & SEND_START_); + YESNO(pDCB->SyncMode & EN_TAG_QUEUEING); + YESNO(pDCB->SyncMode & EN_ATN_STOP); + if (pDCB->SyncOffset & 0x0f) { - spd = 1000/(dcbpnt->NegoPeriod <<2); - spd1 = 1000%(dcbpnt->NegoPeriod <<2); - spd1 = (spd1 * 10)/(dcbpnt->NegoPeriod <<2); - SPRINTF(" %2i.%1i M %02i\n", spd, spd1, (dcbpnt->SyncOffset & 0x0f)); + int sp = pDCB->SyncPeriod; if (! (pDCB->CtrlR3 & FAST_SCSI)) sp++; + SPRINTF(" %03i ns ", (pDCB->NegoPeriod) << 2); + spd = 40/(sp); spd1 = 40%(sp); + spd1 = (spd1 * 10 + sp/2) / (sp); + SPRINTF(" %2i.%1i M %02i\n", spd, spd1, (pDCB->SyncOffset & 0x0f)); } - else SPRINTF("\n"); + else SPRINTF(" (%03i ns)\n", (pDCB->NegoPeriod) << 2); /* Add more info ...*/ - dcbpnt = dcbpnt->pNextDCB; + pDCB = pDCB->pNextDCB; } - restore_flags(flags); + DC390_UNLOCK_ACB; *start = buffer + offset; if (pos - buffer < offset) @@ -1850,81 +2497,93 @@ else return length; } -#endif /* VERSION_ELF_1_2_13 */ +#undef YESNO +#undef SPRINTF #ifdef MODULE /*********************************************************************** - * Function : static int DC390_shutdown (struct Scsi_Host *host) + * Function : static int dc390_shutdown (struct Scsi_Host *host) * * Purpose : does a clean (we hope) shutdown of the SCSI chip. * Use prior to dumping core, unloading the driver, etc. * * Returns : 0 on success ***********************************************************************/ -static int -DC390_shutdown (struct Scsi_Host *host) +static int dc390_shutdown (struct Scsi_Host *host) { UCHAR bval; - USHORT ioport; - unsigned long flags; PACB pACB = (PACB)(host->hostdata); - - ioport = (unsigned int) pACB->IOPortBase; - - save_flags (flags); - cli(); - + /* pACB->soft_reset(host); */ -#ifdef DC390_DEBUG0 - printk("DC390: shutdown,"); -#endif + printk(KERN_INFO "DC390: shutdown\n"); - bval = inb(ioport+CtrlReg1); + pACB->ACBFlag = RESET_DONE; + bval = DC390_read8 (CtrlReg1); bval |= DIS_INT_ON_SCSI_RST; - outb(bval,ioport+CtrlReg1); /* disable interrupt */ - DC390_ResetSCSIBus( pACB ); + DC390_write8 (CtrlReg1, bval); /* disable interrupt */ + if (pACB->Gmode2 & RST_SCSI_BUS) + dc390_ResetSCSIBus (pACB); - restore_flags (flags); return( 0 ); } +void dc390_freeDCBs (struct Scsi_Host *host) +{ + PDCB pDCB, nDCB; + PACB pACB = (PACB)(host->hostdata); + + pDCB = pACB->pLinkDCB; + if (!pDCB) return; + do + { + nDCB = pDCB->pNextDCB; + DCBDEBUG(printk (KERN_INFO "DC390: Free DCB (ID %i, LUN %i): 0x%08x\n",\ + pDCB->UnitSCSIID, pDCB->UnitSCSILUN, (int)pDCB);) + kfree (pDCB); + pDCB = nDCB; + } while (pDCB && pDCB != pACB->pLinkDCB); + +}; int DC390_release(struct Scsi_Host *host) { - int irq_count; - struct Scsi_Host *tmp; + int irq_count; + PACB pACB; + DC390_AFLAGS DC390_IFLAGS +#if USE_SPINLOCKS > 1 + PACB pACB = (PACB)(host->hostdata); +#endif - DC390_shutdown (host); + DC390_LOCK_IO; + DC390_LOCK_ACB; + + dc390_shutdown (host); if (host->irq != IRQ_NONE) { - for (irq_count = 0, tmp = pSH_start; tmp; tmp = tmp->next) + for (irq_count = 0, pACB = dc390_pACB_start; + pACB; pACB = pACB->pNextACB) { - if ( tmp->irq == host->irq ) + if ( pACB->IRQLevel == host->irq ) ++irq_count; } if (irq_count == 1) { -#ifdef DC390_DEBUG0 - printk("DC390: Free IRQ %i.",host->irq); -#endif -#ifndef VERSION_ELF_1_2_13 + DEBUG0(printk(KERN_DEBUG "DC390: Free IRQ %i\n",host->irq);) free_irq(host->irq,NULL); -#else - free_irq(host->irq); -#endif } } release_region(host->io_port,host->n_io_port); - + dc390_freeDCBs (host); + DC390_UNLOCK_ACB; + DC390_UNLOCK_IO; return( 1 ); } Scsi_Host_Template driver_template = DC390_T; #include "scsi_module.c" #endif /* def MODULE */ - diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/scsi/tmscsim.h linux/drivers/scsi/tmscsim.h --- linux.vanilla/drivers/scsi/tmscsim.h Sun Jun 21 18:41:18 1998 +++ linux/drivers/scsi/tmscsim.h Sun Dec 6 03:15:09 1998 @@ -3,12 +3,23 @@ ;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter * ;* Device Driver * ;***********************************************************************/ +/* $Id: tmscsim.h,v 2.1 1998/10/14 10:31:48 garloff Exp $ */ -#ifndef TMSCSIM_H -#define TMSCSIM_H +#ifndef _TMSCSIM_H +#define _TMSCSIM_H #define IRQ_NONE 255 +#define MAX_ADAPTER_NUM 4 +#define MAX_SG_LIST_BUF 16 +#define MAX_CMD_PER_LUN 8 +#define MAX_CMD_QUEUE 2*MAX_CMD_PER_LUN+1 +#define MAX_SCSI_ID 8 +#define MAX_SRB_CNT MAX_CMD_QUEUE+1 /* Max number of started commands */ +#define END_SCAN 2 + +#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ + typedef unsigned char UCHAR; typedef unsigned short USHORT; typedef unsigned long ULONG; @@ -54,17 +65,6 @@ } SGentry1, *PSGE; -#define MAX_ADAPTER_NUM 4 -#define MAX_DEVICES 10 -#define MAX_SG_LIST_BUF 16 -#define MAX_CMD_QUEUE 20 -#define MAX_CMD_PER_LUN 8 -#define MAX_SCSI_ID 8 -#define MAX_SRB_CNT MAX_CMD_QUEUE+4 -#define END_SCAN 2 - -#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */ - /* ;----------------------------------------------------------------------- ; SCSI Request Block @@ -79,40 +79,46 @@ PSCSICMD pcmd; PSGL pSegmentList; -ULONG PhysSRB; +ULONG Segment0[2]; +ULONG Segment1[2]; + +/* 0x2c:*/ ULONG TotalXferredLen; -ULONG SGPhysAddr; /*;a segment starting address */ +ULONG SGBusAddr; /*;a segment starting address as seen by AM53C974A*/ ULONG SGToBeXferLen; /*; to be xfer length */ +ULONG SRBState; +/* 0x3c: */ +UCHAR MsgInBuf[6]; +UCHAR MsgOutBuf[6]; + +/* 0x48: */ SGL Segmentx; /* make a one entry of S/G list table */ PUCHAR pMsgPtr; -USHORT SRBState; -USHORT Revxx2; /* ??? */ -UCHAR MsgInBuf[6]; -UCHAR MsgOutBuf[6]; +UCHAR ScsiCmdLen; +UCHAR ScsiPhase; UCHAR AdaptStatus; UCHAR TargetStatus; + +/* 0x5c: */ UCHAR MsgCnt; UCHAR EndMessage; +UCHAR RetryCnt; +UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */ + /*; b4-settimeout,b5-Residual valid */ UCHAR TagNumber; UCHAR SGcount; UCHAR SGIndex; -UCHAR IORBFlag; /*;81h-Reset, 2-retry */ - UCHAR SRBStatus; -UCHAR RetryCnt; -UCHAR SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */ - /*; b4-settimeout,b5-Residual valid */ -UCHAR ScsiCmdLen; -UCHAR ScsiPhase; -UCHAR Reserved3[3]; /*;for dword alignment */ -ULONG Segment0[2]; -ULONG Segment1[2]; + //UCHAR IORBFlag; /*;81h-Reset, 2-retry */ + +/* 0x64: */ }; + typedef struct _SRB DC390_SRB, *PSRB; /* @@ -129,42 +135,44 @@ PSCSICMD pQIORBtail; PSCSICMD AboIORBhead; PSCSICMD AboIORBtail; -USHORT QIORBCnt; -USHORT AboIORBcnt; +ULONG QIORBCnt; +ULONG AboIORBcnt; +/* 0x20: */ PSRB pWaitingSRB; PSRB pWaitLast; PSRB pGoingSRB; PSRB pGoingLast; PSRB pActiveSRB; -USHORT GoingSRBCnt; -USHORT WaitSRBCnt; /* ??? */ +UCHAR GoingSRBCnt; +UCHAR WaitSRBCnt; /* ??? */ +UCHAR DevType; +UCHAR MaxCommand; +/* 0x38: */ ULONG TagMask; -USHORT MaxCommand; -USHORT AdaptIndex; /*; UnitInfo struc start */ -USHORT UnitIndex; /*; nth Unit on this card */ UCHAR UnitSCSIID; /*; SCSI Target ID (SCSI Only) */ UCHAR UnitSCSILUN; /*; SCSI Log. Unit (SCSI Only) */ - +UCHAR DevMode; UCHAR IdentifyMsg; + UCHAR CtrlR1; UCHAR CtrlR3; UCHAR CtrlR4; -UCHAR InqDataBuf[8]; -UCHAR CapacityBuf[8]; -UCHAR DevMode; -UCHAR AdpMode; +UCHAR DCBFlag; + +/* 0x44: */ UCHAR SyncMode; /*; 0:async mode */ UCHAR NegoPeriod; /*;for nego. */ UCHAR SyncPeriod; /*;for reg. */ UCHAR SyncOffset; /*;for reg. and nego.(low nibble) */ -UCHAR UnitCtrlFlag; -UCHAR DCBFlag; -UCHAR DevType; -UCHAR Reserved2[3]; /*;for dword alignment */ + +/* 0x48:*/ +//UCHAR InqDataBuf[8]; +//UCHAR CapacityBuf[8]; +/* 0x58: */ }; typedef struct _DCB DC390_DCB, *PDCB; @@ -175,40 +183,55 @@ */ struct _ACB { -ULONG PhysACB; PSH pScsiHost; struct _ACB *pNextACB; USHORT IOPortBase; -USHORT Revxx1; /* ??? */ +UCHAR IRQLevel; +UCHAR status; + +UCHAR SRBCount; +UCHAR AdapterIndex; /*; nth Adapter this driver */ +UCHAR DeviceCnt; +UCHAR DCBCnt; + +/* 0x10: */ +UCHAR TagMaxNum; +UCHAR ACBFlag; +UCHAR Gmode2; +UCHAR scan_devices; PDCB pLinkDCB; +PDCB pLastDCB; PDCB pDCBRunRobin; PDCB pActiveDCB; -PDCB pDCB_free; PSRB pFreeSRB; PSRB pTmpSRB; -USHORT SRBCount; -USHORT AdapterIndex; /*; nth Adapter this driver */ -USHORT max_id; -USHORT max_lun; + +/* 0x2c: */ UCHAR msgin123[4]; -UCHAR status; -UCHAR AdaptSCSIID; /*; Adapter SCSI Target ID */ -UCHAR AdaptSCSILUN; /*; Adapter SCSI LUN */ -UCHAR DeviceCnt; -UCHAR IRQLevel; -UCHAR TagMaxNum; -UCHAR ACBFlag; -UCHAR Gmode2; -UCHAR LUNchk; -UCHAR scan_devices; -UCHAR HostID_Bit; -UCHAR Reserved1[1]; /*;for dword alignment */ UCHAR DCBmap[MAX_SCSI_ID]; -DC390_DCB DCB_array[MAX_DEVICES]; /* +74h, Len=3E8 */ -DC390_SRB SRB_array[MAX_SRB_CNT]; /* +45Ch, Len= */ + +#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(__SMP__) || DEBUG_SPINLOCKS > 0) +spinlock_t lock; +#endif +UCHAR sel_timeout; +UCHAR glitch_cfg; + +UCHAR reserved[2]; /* alignment */ + +PDEVDECL1; /* Pointer to PCI cfg. space */ +/* 0x44/0x40: */ +ULONG Cmds; +ULONG CmdInQ; +ULONG CmdOutOfSRB; +ULONG SelLost; + +/* 0x50/0x4c: */ DC390_SRB TmpSRB; +/* 0xb4/0xb0: */ +DC390_SRB SRB_array[MAX_SRB_CNT]; /* 18 SRBs */ +/* 0x7bc/0x7b8: */ }; typedef struct _ACB DC390_ACB, *PACB; @@ -278,14 +301,6 @@ #define DO_SYNC_NEGO BIT13 #define SRB_UNEXPECT_RESEL BIT14 -/*;---ACBFlag */ -#define RESET_DEV BIT0 -#define RESET_DETECT BIT1 -#define RESET_DONE BIT2 - -/*;---DCBFlag */ -#define ABORT_DEV_ BIT0 - /*;---SRBstatus */ #define SRB_OK BIT0 #define ABORTION BIT1 @@ -294,6 +309,14 @@ #define PARITY_ERROR BIT4 #define SRB_ERROR BIT5 +/*;---ACBFlag */ +#define RESET_DEV BIT0 +#define RESET_DETECT BIT1 +#define RESET_DONE BIT2 + +/*;---DCBFlag */ +#define ABORT_DEV_ BIT0 + /*;---SRBFlag */ #define DATAOUT BIT7 #define DATAIN BIT6 @@ -316,7 +339,7 @@ #define H_BAD_CCB_OR_SG 0x1A #define H_ABORT 0x0FF -/*; SCSI Status byte codes*/ +/*; SCSI Status byte codes*/ /* Twice the values defined in scsi/scsi.h */ #define SCSI_STAT_GOOD 0x0 /*; Good status */ #define SCSI_STAT_CHECKCOND 0x02 /*; SCSI Check Condition */ #define SCSI_STAT_CONDMET 0x04 /*; Condition Met */ @@ -335,9 +358,9 @@ #define SYNC_DISABLE 0 #define SYNC_ENABLE BIT0 #define SYNC_NEGO_DONE BIT1 -#define WIDE_ENABLE BIT2 -#define WIDE_NEGO_DONE BIT3 -#define EN_TAG_QUEUING BIT4 +#define WIDE_ENABLE BIT2 /* Not used ;-) */ +#define WIDE_NEGO_DONE BIT3 /* Not used ;-) */ +#define EN_TAG_QUEUEING BIT4 #define EN_ATN_STOP BIT5 #define SYNC_NEGO_OFFSET 15 @@ -352,7 +375,7 @@ #define SCSI_MSG_OUT 6 #define SCSI_MSG_IN 7 -/*;----SCSI MSG BYTE*/ +/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */ #define MSG_COMPLETE 0x00 #define MSG_EXTENDED 0x01 #define MSG_SAVE_PTR 0x02 @@ -373,13 +396,6 @@ #define MSG_IDENTIFY 0x80 #define MSG_HOST_ID 0x0C0 -/*;----SCSI STATUS BYTE*/ -#define STATUS_GOOD 0x00 -#define CHECK_CONDITION_ 0x02 -#define STATUS_BUSY 0x08 -#define STATUS_INTERMEDIATE 0x10 -#define RESERVE_CONFLICT 0x18 - /* cmd->result */ #define STATUS_MASK_ 0xFF #define MSG_MASK 0xFF00 @@ -389,7 +405,7 @@ ** Inquiry Data format */ -typedef struct _SCSIInqData { /* INQ */ +typedef struct _SCSIInqData { /* INQUIRY */ UCHAR DevType; /* Periph Qualifier & Periph Dev Type*/ UCHAR RMB_TypeMod; /* rem media bit & Dev Type Modifier */ @@ -412,6 +428,7 @@ #define SCSI_DEVTYPE 0x1F /* Peripheral Device Type */ #define SCSI_PERIPHQUAL 0xE0 /* Peripheral Qualifier */ +#define TYPE_NODEV SCSI_DEVTYPE /* Unknown or no device type */ /* Inquiry byte 1 mask */ @@ -420,18 +437,10 @@ /* Peripheral Device Type definitions */ +/* see include/scsi/scsi.h for the rest */ -#define SCSI_DASD 0x00 /* Direct-access Device */ -#define SCSI_SEQACESS 0x01 /* Sequential-access device */ -#define SCSI_PRINTER 0x02 /* Printer device */ -#define SCSI_PROCESSOR 0x03 /* Processor device */ -#define SCSI_WRITEONCE 0x04 /* Write-once device */ -#define SCSI_CDROM 0x05 /* CD-ROM device */ -#define SCSI_SCANNER 0x06 /* Scanner device */ -#define SCSI_OPTICAL 0x07 /* Optical memory device */ -#define SCSI_MEDCHGR 0x08 /* Medium changer device */ -#define SCSI_COMM 0x09 /* Communications device */ -#define SCSI_NODEV 0x1F /* Unknown or no device type */ +#define TYPE_PRINTER 0x02 /* Printer device */ +#define TYPE_COMM 0x09 /* Communications device */ /* ** Inquiry flag definitions (Inq data byte 7) @@ -459,17 +468,24 @@ UCHAR xx2; } EEprom, *PEEprom; -#define EE_ADAPT_SCSI_ID 64 -#define EE_MODE2 65 -#define EE_DELAY 66 -#define EE_TAG_CMD_NUM 67 +#define REAL_EE_ADAPT_SCSI_ID 64 +#define REAL_EE_MODE2 65 +#define REAL_EE_DELAY 66 +#define REAL_EE_TAG_CMD_NUM 67 + +#define EE_ADAPT_SCSI_ID 32 +#define EE_MODE2 33 +#define EE_DELAY 34 +#define EE_TAG_CMD_NUM 35 + +#define EE_LEN 40 /*; EE_MODE1 bits definition*/ #define PARITY_CHK_ BIT0 #define SYNC_NEGO_ BIT1 #define EN_DISCONNECT_ BIT2 #define SEND_START_ BIT3 -#define TAG_QUEUING_ BIT4 +#define TAG_QUEUEING_ BIT4 /*; EE_MODE2 bits definition*/ #define MORE2_DRV BIT0 @@ -494,34 +510,42 @@ ;==================== */ -/*; Command Reg.(+0CH) */ +/*; Command Reg.(+0CH) (rw) */ #define DMA_COMMAND BIT7 #define NOP_CMD 0 #define CLEAR_FIFO_CMD 1 #define RST_DEVICE_CMD 2 #define RST_SCSI_BUS_CMD 3 + #define INFO_XFER_CMD 0x10 #define INITIATOR_CMD_CMPLTE 0x11 #define MSG_ACCEPTED_CMD 0x12 #define XFER_PAD_BYTE 0x18 #define SET_ATN_CMD 0x1A #define RESET_ATN_CMD 0x1B -#define SELECT_W_ATN 0x42 + +#define SEL_WO_ATN 0x41 /* currently not used */ +#define SEL_W_ATN 0x42 #define SEL_W_ATN_STOP 0x43 +#define SEL_W_ATN3 0x46 #define EN_SEL_RESEL 0x44 -#define SEL_W_ATN2 0x46 +#define DIS_SEL_RESEL 0x45 /* currently not used */ +#define RESEL 0x40 /* " */ +#define RESEL_ATN3 0x47 /* " */ + #define DATA_XFER_CMD INFO_XFER_CMD -/*; SCSI Status Reg.(+10H) */ +/*; SCSI Status Reg.(+10H) (r) */ #define INTERRUPT BIT7 #define ILLEGAL_OP_ERR BIT6 #define PARITY_ERR BIT5 #define COUNT_2_ZERO BIT4 #define GROUP_CODE_VALID BIT3 -#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0) +#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0) +/* BIT2: MSG phase; BIT1: C/D physe; BIT0: I/O phase */ -/*; Interrupt Status Reg.(+14H) */ +/*; Interrupt Status Reg.(+14H) (r) */ #define SCSI_RESET BIT7 #define INVALID_CMD BIT6 #define DISCONNECTED BIT5 @@ -531,11 +555,12 @@ #define SEL_ATTENTION BIT1 #define SELECTED BIT0 -/*; Internal State Reg.(+18H) */ +/*; Internal State Reg.(+18H) (r) */ #define SYNC_OFFSET_FLAG BIT3 #define INTRN_STATE_MASK (BIT2+BIT1+BIT0) +/* 0x04: Sel. successful (w/o stop), 0x01: Sel. successful (w/ stop) */ -/*; Clock Factor Reg.(+24H) */ +/*; Clock Factor Reg.(+24H) (w) */ #define CLK_FREQ_40MHZ 0 #define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0) #define CLK_FREQ_30MHZ (BIT2+BIT1) @@ -544,47 +569,54 @@ #define CLK_FREQ_15MHZ (BIT1+BIT0) #define CLK_FREQ_10MHZ BIT1 -/*; Control Reg. 1(+20H) */ +/*; Control Reg. 1(+20H) (rw) */ #define EXTENDED_TIMING BIT7 #define DIS_INT_ON_SCSI_RST BIT6 #define PARITY_ERR_REPO BIT4 -#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) +#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) /* host adapter ID */ -/*; Control Reg. 2(+2CH) */ +/*; Control Reg. 2(+2CH) (rw) */ #define EN_FEATURE BIT6 #define EN_SCSI2_CMD BIT3 -/*; Control Reg. 3(+30H) */ +/*; Control Reg. 3(+30H) (rw) */ #define ID_MSG_CHECK BIT7 #define EN_QTAG_MSG BIT6 #define EN_GRP2_CMD BIT5 #define FAST_SCSI BIT4 /* ;10MB/SEC */ #define FAST_CLK BIT3 /* ;25 - 40 MHZ */ -/*; Control Reg. 4(+34H) */ +/*; Control Reg. 4(+34H) (rw) */ #define EATER_12NS 0 #define EATER_25NS BIT7 #define EATER_35NS BIT6 #define EATER_0NS (BIT7+BIT6) +#define REDUCED_POWER BIT5 +#define CTRL4_RESERVED BIT4 /* must be 1 acc. to AM53C974.c */ #define NEGATE_REQACKDATA BIT2 #define NEGATE_REQACK BIT3 + +#define GLITCH_TO_NS(x) (((~x>>6 & 2) >> 1) | ((x>>6 & 1) << 1 ^ (x>>6 & 2))) +#define NS_TO_GLITCH(y) (((~y<<7) | ~((y<<6) ^ ((y<<5 & 1<<6) | ~0x40))) & 0xc0) + /* ;==================== ; DMA Register ;==================== */ -/*; DMA Command Reg.(+40H) */ +/*; DMA Command Reg.(+40H) (rw) */ #define READ_DIRECTION BIT7 #define WRITE_DIRECTION 0 #define EN_DMA_INT BIT6 -#define MAP_TO_MDL BIT5 -#define DIAGNOSTIC BIT4 +#define EN_PAGE_INT BIT5 /* page transfer interrupt enable */ +#define MAP_TO_MDL BIT4 +#define DIAGNOSTIC BIT2 #define DMA_IDLE_CMD 0 #define DMA_BLAST_CMD BIT0 #define DMA_ABORT_CMD BIT1 #define DMA_START_CMD (BIT1+BIT0) -/*; DMA Status Reg.(+54H) */ +/*; DMA Status Reg.(+54H) (r) */ #define PCI_MS_ABORT BIT6 #define BLAST_COMPLETE BIT5 #define SCSI_INTERRUPT BIT4 @@ -593,88 +625,77 @@ #define DMA_XFER_ERROR BIT1 #define POWER_DOWN BIT0 -/* -; DMA SCSI Bus and Ctrl.(+70H) -;EN_INT_ON_PCI_ABORT -*/ +/*; DMA SCSI Bus and Ctrl.(+70H) */ +#define EN_INT_ON_PCI_ABORT BIT25 +#define WRT_ERASE_DMA_STAT BIT24 +#define PW_DOWN_CTRL BIT21 +#define SCSI_BUSY BIT20 +#define SCLK BIT19 +#define SCAM BIT18 +#define SCSI_LINES 0x0003ffff /* ;========================================================== ; SCSI Chip register address offset ;========================================================== +;Registers are rw unless declared otherwise */ -#define CtcReg_Low 0x00 -#define CtcReg_Mid 0x04 +#define CtcReg_Low 0x00 /* r curr. transfer count */ +#define CtcReg_Mid 0x04 /* r */ +#define CtcReg_High 0x38 /* r */ #define ScsiFifo 0x08 #define ScsiCmd 0x0C -#define Scsi_Status 0x10 -#define INT_Status 0x14 -#define Sync_Period 0x18 -#define Sync_Offset 0x1C -#define CtrlReg1 0x20 -#define Clk_Factor 0x24 +#define Scsi_Status 0x10 /* r */ +#define INT_Status 0x14 /* r */ +#define Sync_Period 0x18 /* w */ +#define Sync_Offset 0x1C /* w */ +#define Clk_Factor 0x24 /* w */ +#define CtrlReg1 0x20 #define CtrlReg2 0x2C #define CtrlReg3 0x30 #define CtrlReg4 0x34 -#define CtcReg_High 0x38 #define DMA_Cmd 0x40 -#define DMA_XferCnt 0x44 -#define DMA_XferAddr 0x48 -#define DMA_Wk_ByteCntr 0x4C -#define DMA_Wk_AddrCntr 0x50 -#define DMA_Status 0x54 -#define DMA_MDL_Addr 0x58 -#define DMA_Wk_MDL_Cntr 0x5C -#define DMA_ScsiBusCtrl 0x70 - -#define StcReg_Low CtcReg_Low -#define StcReg_Mid CtcReg_Mid -#define Scsi_Dest_ID Scsi_Status -#define Scsi_TimeOut INT_Status -#define Intern_State Sync_Period -#define Current_Fifo Sync_Offset -#define StcReg_High CtcReg_High - -#define am_target Scsi_Status -#define am_timeout INT_Status -#define am_seq_step Sync_Period -#define am_fifo_count Sync_Offset - - -#define DC390_read8(address) \ - inb(DC390_ioport + (address))) - -#define DC390_read16(address) \ - inw(DC390_ioport + (address))) - -#define DC390_read32(address) \ - inl(DC390_ioport + (address))) - -#define DC390_write8(address,value) \ - outb((value), DC390_ioport + (address))) - -#define DC390_write16(address,value) \ - outw((value), DC390_ioport + (address))) - -#define DC390_write32(address,value) \ - outl((value), DC390_ioport + (address))) - - -/* Configuration method #1 */ -#define PCI_CFG1_ADDRESS_REG 0xcf8 -#define PCI_CFG1_DATA_REG 0xcfc -#define PCI_CFG1_ENABLE 0x80000000 -#define PCI_CFG1_TUPPLE(bus, device, function, register) \ - (PCI_CFG1_ENABLE | (((bus) << 16) & 0xff0000) | \ - (((device) << 11) & 0xf800) | (((function) << 8) & 0x700)| \ - (((register) << 2) & 0xfc)) - -/* Configuration method #2 */ -#define PCI_CFG2_ENABLE_REG 0xcf8 -#define PCI_CFG2_FORWARD_REG 0xcfa -#define PCI_CFG2_ENABLE 0x0f0 -#define PCI_CFG2_TUPPLE(function) \ - (PCI_CFG2_ENABLE | (((function) << 1) & 0xe)) +#define DMA_XferCnt 0x44 /* rw starting transfer count (32 bit) */ +#define DMA_XferAddr 0x48 /* rw starting physical address (32 bit) */ +#define DMA_Wk_ByteCntr 0x4C /* r working byte counter */ +#define DMA_Wk_AddrCntr 0x50 /* r working address counter */ +#define DMA_Status 0x54 /* r */ +#define DMA_MDL_Addr 0x58 /* rw starting MDL address */ +#define DMA_Wk_MDL_Cntr 0x5C /* r working MDL counter */ +#define DMA_ScsiBusCtrl 0x70 /* rw SCSI Bus, PCI/DMA Ctrl */ + +#define StcReg_Low CtcReg_Low /* w start transfer count */ +#define StcReg_Mid CtcReg_Mid /* w */ +#define StcReg_High CtcReg_High /* w */ +#define Scsi_Dest_ID Scsi_Status /* w */ +#define Scsi_TimeOut INT_Status /* w */ +#define Intern_State Sync_Period /* r */ +#define Current_Fifo Sync_Offset /* r Curr. FIFO / int. state */ + + +#define DC390_read8(address) \ + (inb (pACB->IOPortBase + (address))) + +#define DC390_read8_(address, base) \ + (inb ((USHORT)(base) + (address))) + +#define DC390_read16(address) \ + (inw (pACB->IOPortBase + (address))) + +#define DC390_read32(address) \ + (inl (pACB->IOPortBase + (address))) + +#define DC390_write8(address,value) \ + outb ((value), pACB->IOPortBase + (address)) + +#define DC390_write8_(address,value,base) \ + outb ((value), (USHORT)(base) + (address)) + +#define DC390_write16(address,value) \ + outw ((value), pACB->IOPortBase + (address)) + +#define DC390_write32(address,value) \ + outl ((value), pACB->IOPortBase + (address)) -#endif /* TMSCSIM_H */ +#endif /* _TMSCSIM_H */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/drivers/sound/Config.in linux/drivers/sound/Config.in --- linux.vanilla/drivers/sound/Config.in Sun Jun 21 18:41:20 1998 +++ linux/drivers/sound/Config.in Sun Dec 6 17:38:51 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 --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/Config.in linux/fs/Config.in --- linux.vanilla/fs/Config.in Fri Jul 24 17:29:04 1998 +++ linux/fs/Config.in Fri Dec 11 21:01:17 1998 @@ -19,32 +19,32 @@ dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS - dep_tristate 'Codepage 437' CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS - dep_tristate 'Codepage 737' CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS - dep_tristate 'Codepage 775' CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS - dep_tristate 'Codepage 850' CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS - dep_tristate 'Codepage 852' CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS - dep_tristate 'Codepage 855' CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS - dep_tristate 'Codepage 857' CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS - dep_tristate 'Codepage 860' CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS - dep_tristate 'Codepage 861' CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS - dep_tristate 'Codepage 862' CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS - dep_tristate 'Codepage 863' CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS - dep_tristate 'Codepage 864' CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS - dep_tristate 'Codepage 865' CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS - dep_tristate 'Codepage 866' CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS - dep_tristate 'Codepage 869' CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS - dep_tristate 'Codepage 874' CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-1' CONFIG_NLS_ISO8859_1 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-2' CONFIG_NLS_ISO8859_2 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-3' CONFIG_NLS_ISO8859_3 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-4' CONFIG_NLS_ISO8859_4 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-5' CONFIG_NLS_ISO8859_5 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-6' CONFIG_NLS_ISO8859_6 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-7' CONFIG_NLS_ISO8859_7 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-8' CONFIG_NLS_ISO8859_8 $CONFIG_NLS - dep_tristate 'NLS ISO 8859-9' CONFIG_NLS_ISO8859_9 $CONFIG_NLS - dep_tristate 'NLS KOI8-R' CONFIG_NLS_KOI8_R $CONFIG_NLS + dep_tristate 'Codepage 437 (United States, Canada)' CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS + dep_tristate 'Codepage 737 (Greek)' CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS + dep_tristate 'Codepage 775 (Baltic Rim)' CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS + dep_tristate 'Codepage 850 (Europe)' CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS + dep_tristate 'Codepage 852 (Central/Eastern Europe)' CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS + dep_tristate 'Codepage 855 (Cyrillic)' CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS + dep_tristate 'Codepage 857 (Turkish)' CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS + dep_tristate 'Codepage 860 (Portugese)' CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS + dep_tristate 'Codepage 861 (Icelandic)' CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS + dep_tristate 'Codepage 862 (Hebrew)' CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS + dep_tristate 'Codepage 863 (Canadian French)' CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS + dep_tristate 'Codepage 864 (Arabic)' CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS + dep_tristate 'Codepage 865 (Nordic European)' CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS + dep_tristate 'Codepage 866 (Cyrillic/Russian)' CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS + dep_tristate 'Codepage 869 (Greek)' CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS + dep_tristate 'Codepage 874 (Thai)' CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-1 (Latin 1; Western Europe)' CONFIG_NLS_ISO8859_1 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-2 (Latin 2; Slavic/Central European)' CONFIG_NLS_ISO8859_2 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish)' CONFIG_NLS_ISO8859_3 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-4 (Latin 4; Estonian, Latvian, Lithuanian)' CONFIG_NLS_ISO8859_4 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-5 (Cyrillic)' CONFIG_NLS_ISO8859_5 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-6 (Arabic)' CONFIG_NLS_ISO8859_6 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-8 (Hebrew)' CONFIG_NLS_ISO8859_8 $CONFIG_NLS + dep_tristate 'NLS ISO 8859-9 (Latin 5; Turkey)' CONFIG_NLS_ISO8859_9 $CONFIG_NLS + dep_tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R $CONFIG_NLS fi bool '/proc filesystem support' CONFIG_PROC_FS diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/isofs/inode.c linux/fs/isofs/inode.c --- linux.vanilla/fs/isofs/inode.c Sun Dec 6 00:14:45 1998 +++ linux/fs/isofs/inode.c Fri Dec 11 20:31:33 1998 @@ -824,8 +824,9 @@ } /* There are defective discs out there - we do this to protect - ourselves. A cdrom will never contain more than 800Mb */ - if((inode->i_size < 0 || inode->i_size > 800000000) && + ourselves. A cdrom will never contain more than 800Mb + Allow 1Gig for DVD however - Ulrich Habel */ + if((inode->i_size < 0 || inode->i_size > 1073741824) && inode->i_sb->u.isofs_sb.s_cruft == 'n') { printk("Warning: defective cdrom. Enabling \"cruft\" mount option.\n"); inode->i_sb->u.isofs_sb.s_cruft = 'y'; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/fs/read_write.c linux/fs/read_write.c --- linux.vanilla/fs/read_write.c Sun Dec 6 00:14:46 1998 +++ linux/fs/read_write.c Fri Dec 11 20:33:58 1998 @@ -254,7 +254,7 @@ return retval; } - retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, + retval = locks_verify_area(type == VERIFY_WRITE ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, file->f_pos, tot_len); if (retval) return retval; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/page.h linux/include/asm-i386/page.h --- linux.vanilla/include/asm-i386/page.h Sun Jun 21 18:40:58 1998 +++ linux/include/asm-i386/page.h Fri Dec 11 20:35:53 1998 @@ -7,6 +7,7 @@ #define PAGE_MASK (~(PAGE_SIZE-1)) #ifdef __KERNEL__ +#ifndef __ASSEMBLY__ #define STRICT_MM_TYPECHECKS @@ -49,12 +50,14 @@ #define __pgprot(x) (x) #endif +#endif /* !__ASSEMBLY__ */ /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) /* This handles the memory map.. */ -#define PAGE_OFFSET 0 +#define __PAGE_OFFSET ((0x1000-CONFIG_MAX_MEMSIZE)<<20) +#define PAGE_OFFSET (0) #define MAP_NR(addr) (((unsigned long)(addr)) >> PAGE_SHIFT) #endif /* __KERNEL__ */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h --- linux.vanilla/include/asm-i386/pgtable.h Sun Jun 21 18:40:58 1998 +++ linux/include/asm-i386/pgtable.h Fri Dec 11 21:04:05 1998 @@ -19,6 +19,8 @@ * the i386 page table tree. */ +#ifndef __ASSEMBLY__ + /* Caches aren't brain-dead on the intel. */ #define flush_cache_all() do { } while (0) #define flush_cache_mm(mm) do { } while (0) @@ -46,13 +48,13 @@ * NOTE! The intel "invlpg" semantics are extremely strange. The * chip will add the segment base to the memory address, even though * no segment checking is done. We correct for this by using an - * offset of 0x40000000 that will wrap around the kernel segment base - * of 0xC0000000 to get the correct address (it will always be outside + * offset of -__PAGE_OFFSET that will wrap around the kernel segment base + * of __PAGE_OFFSET to get the correct address (it will always be outside * the kernel segment, but we're only interested in the final linear * address. */ #define __invlpg_mem(addr) \ - (((char *)(addr))[0x40000000]) + (((char *)(addr)-__PAGE_OFFSET)) #define __invlpg(addr) \ __asm__ __volatile__("invlpg %0": :"m" (__invlpg_mem(addr))) @@ -177,6 +179,7 @@ } #endif #endif +#endif /* !__ASSEMBLY__ */ /* Certain architectures need to do special things when pte's @@ -203,6 +206,17 @@ #define PTRS_PER_PMD 1 #define PTRS_PER_PGD 1024 +/* + * pgd entries used up by user/kernel: + */ + +#define USER_PGD_PTRS ((unsigned long)__PAGE_OFFSET >> PGDIR_SHIFT) +#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS) +#define __USER_PGD_PTRS (__PAGE_OFFSET >> PGDIR_SHIFT) +#define __KERNEL_PGD_PTRS (PTRS_PER_PGD-__USER_PGD_PTRS) + +#ifndef __ASSEMBLY__ + /* Just any arbitrary offset to the start of the vmalloc VM area: the * current 8MB value just means that there will be a 8MB "hole" after the * physical memory until the kernel virtual memory starts. That means that @@ -507,5 +521,7 @@ #define SWP_TYPE(entry) (((entry) >> 1) & 0x7f) #define SWP_OFFSET(entry) ((entry) >> 8) #define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8)) + +#endif /* !__ASSEMBLY__ */ #endif /* _I386_PAGE_H */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/processor.h linux/include/asm-i386/processor.h --- linux.vanilla/include/asm-i386/processor.h Sun Dec 6 00:14:46 1998 +++ linux/include/asm-i386/processor.h Fri Dec 11 20:35:53 1998 @@ -83,10 +83,9 @@ #define MCA_bus__is_a_macro /* for versions in ksyms.c */ /* - * User space process size: 3GB. This is hardcoded into a few places, - * so don't change it unless you know what you are doing. + * User space process size: 3GB (default). */ -#define TASK_SIZE (0xC0000000UL) +#define TASK_SIZE ((unsigned long)__PAGE_OFFSET) #define MAX_USER_ADDR TASK_SIZE #define MMAP_SEARCH_START (TASK_SIZE/3) diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/asm-i386/system.h linux/include/asm-i386/system.h --- linux.vanilla/include/asm-i386/system.h Sun Dec 6 00:14:46 1998 +++ linux/include/asm-i386/system.h Fri Dec 11 20:35:53 1998 @@ -275,7 +275,7 @@ "movb %%ah,%6\n\t" \ "rorl $16,%%eax" \ : /* no output */ \ - :"a" (addr+0xc0000000), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ + :"a" (addr+__PAGE_OFFSET), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \ "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \ ) diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/blkdev.h linux/include/linux/blkdev.h --- linux.vanilla/include/linux/blkdev.h Fri Jul 24 17:29:05 1998 +++ linux/include/linux/blkdev.h Fri Dec 11 21:03:58 1998 @@ -25,6 +25,7 @@ int errors; unsigned long sector; unsigned long nr_sectors; + unsigned long nr_segments; unsigned long current_nr_sectors; char * buffer; struct semaphore * sem; @@ -62,5 +63,13 @@ extern int * blksize_size[MAX_BLKDEV]; extern int * hardsect_size[MAX_BLKDEV]; + +extern int * max_sectors[MAX_BLKDEV]; + +extern int * max_segments[MAX_BLKDEV]; + +#define MAX_SECTORS 254 + +#define MAX_SEGMENTS MAX_SECTORS #endif diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/icmp.h linux/include/linux/icmp.h --- linux.vanilla/include/linux/icmp.h Sun Jun 21 18:40:57 1998 +++ linux/include/linux/icmp.h Sun Dec 6 03:18:55 1998 @@ -76,10 +76,13 @@ }; +#ifdef __KERNEL__ + struct icmp_err { int errno; unsigned fatal:1; }; +#endif #endif /* _LINUX_ICMP_H */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/major.h linux/include/linux/major.h --- linux.vanilla/include/linux/major.h Fri Jul 24 17:29:05 1998 +++ linux/include/linux/major.h Sun Dec 6 03:32:15 1998 @@ -65,6 +65,7 @@ #define IDETAPE_MAJOR 37 #define Z2RAM_MAJOR 37 #define RISCOM8_NORMAL_MAJOR 48 +#define DAC960_MAJOR 48 /* 48..55 */ #define RISCOM8_CALLOUT_MAJOR 49 #define MKISS_MAJOR 55 #define APBLOCK_MAJOR 60 /* AP1000 Block device */ diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/pci.h linux/include/linux/pci.h --- linux.vanilla/include/linux/pci.h Sun Dec 6 00:14:46 1998 +++ linux/include/linux/pci.h Fri Dec 11 20:20:00 1998 @@ -470,6 +470,11 @@ #define PCI_DEVICE_ID_PICOP_PT86C52X 0x0001 #define PCI_DEVICE_ID_PICOP_PT80C524 0x8002 +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_DEVICE_ID_MYLEX_DAC960P_V2 0x0001 +#define PCI_DEVICE_ID_MYLEX_DAC960P_V3 0x0002 +#define PCI_DEVICE_ID_MYLEX_DAC960P_V4 0x0010 + #define PCI_VENDOR_ID_APPLE 0x106b #define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 #define PCI_DEVICE_ID_APPLE_GC 0x0002 @@ -663,6 +668,9 @@ #define PCI_DEVICE_ID_VIA_86C100A 0x6100 #define PCI_DEVICE_ID_VIA_82C597_1 0x8597 +#define PCI_VENDOR_ID_SMC2 0x1113 +#define PCI_DEVICE_ID_SMC2_1211TX 0x1211 + #define PCI_VENDOR_ID_VORTEX 0x1119 #define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 #define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 @@ -893,12 +901,16 @@ #define PCI_DEVICE_ID_S3_ViRGE_MXPMV 0x8c03 #define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 + #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82375 0x0482 #define PCI_DEVICE_ID_INTEL_82424 0x0483 #define PCI_DEVICE_ID_INTEL_82378 0x0484 #define PCI_DEVICE_ID_INTEL_82430 0x0486 #define PCI_DEVICE_ID_INTEL_82434 0x04a3 +#define PCI_DEVICE_ID_INTEL_I960 0x0960 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_82092AA_1 0x1222 #define PCI_DEVICE_ID_INTEL_7116 0x1223 diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- linux.vanilla/include/linux/proc_fs.h Sun Dec 6 00:14:46 1998 +++ linux/include/linux/proc_fs.h Fri Dec 11 21:04:04 1998 @@ -143,12 +143,14 @@ PROC_SCSI_NCR53C406A, PROC_SCSI_MEGARAID, PROC_SCSI_PPA, + PROC_SCSI_ATP870U, PROC_SCSI_ESP, PROC_SCSI_A3000, PROC_SCSI_A2091, PROC_SCSI_GVP11, PROC_SCSI_ATARI, PROC_SCSI_GDTH, + PROC_SCSI_INI9100U, PROC_SCSI_IDESCSI, PROC_SCSI_SCSI_DEBUG, PROC_SCSI_NOT_PRESENT, diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/include/linux/serial.h linux/include/linux/serial.h --- linux.vanilla/include/linux/serial.h Sun Dec 6 00:14:46 1998 +++ linux/include/linux/serial.h Fri Dec 11 21:04:04 1998 @@ -81,6 +81,7 @@ #define ASYNC_CTS_FLOW 0x04000000 /* Do CTS flow control */ #define ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */ #define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards */ +#define ASYNC_PCI 0x00800000 /* this port is on a PCI board */ /* * Multiport serial configuration structure --- external structure @@ -200,6 +201,42 @@ unsigned char mask4, match4; int port_monitor; }; + +/* + * PCI board base -- internal structure + */ + +/* + * There are four config spaces on a PCI board. Which ones house the + * serial chips? + */ + +enum pci_spc { + pci_space_0 = 1, + pci_space_1 = 2, + pci_space_2 = 4, + pci_space_3 = 8 +}; + +struct pci_struct { + int start; /* IO Base for this chip */ + struct pci_serial_boards *type; /* Pointer to the board type */ +}; + +struct pci_serial_boards { + int vendor_id; /* Vendor of the Board */ + int device_id; /* Device ID */ + char *board_name; /* Name of the Board */ + int board_type; /* Which chip is on the board */ + enum pci_spc pci_space; /* How many PCI spaces should be mapped in? */ + int dev_per_space; /* How many chips are in every PCI space */ + int dev_spacing; /* What spacing are they located? */ + int io_size; /* Size of the Register IO Space */ + int baud_base; /* What crystal is on the board */ +}; + + + /* Export to allow PCMCIA to use this - Dave Hinds */ extern int register_serial(struct serial_struct *req); diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/init/main.c linux/init/main.c --- linux.vanilla/init/main.c Sun Dec 6 00:14:47 1998 +++ linux/init/main.c Sun Dec 6 03:32:15 1998 @@ -104,6 +104,7 @@ extern void in2000_setup(char *str, int *ints); extern void NCR53c406a_setup(char *str, int *ints); extern void wd7000_setup(char *str, int *ints); +extern void dc390_setup(char* str, int *ints); extern void ppa_setup(char *str, int *ints); extern void scsi_luns_setup(char *str, int *ints); extern void sound_setup(char *str, int *ints); @@ -384,6 +385,9 @@ #ifdef CONFIG_SCSI_PPA { "ppa=", ppa_setup }, #endif +#if defined(CONFIG_SCSI_DC390T) && ! defined(CONFIG_SCSI_DC390T_NOGENSUPP) + { "tmscsim=", dc390_setup }, +#endif #ifdef CONFIG_BLK_DEV_XD { "xd=", xd_setup }, { "xd_geo=", xd_manual_geo_init }, @@ -648,6 +652,24 @@ { "sdn", 0x08d0 }, { "sdo", 0x08e0 }, { "sdp", 0x08f0 }, +#ifdef CONFIG_BLK_DEV_DAC960 + { "rd/c0d0p",0x3000 }, + { "rd/c0d1p",0x3008 }, + { "rd/c0d2p",0x3010 }, + { "rd/c0d3p",0x3018 }, + { "rd/c0d4p",0x3020 }, + { "rd/c0d5p",0x3028 }, + { "rd/c0d6p",0x3030 }, + { "rd/c0d7p",0x3038 }, + { "rd/c0d8p",0x3040 }, + { "rd/c0d9p",0x3048 }, + { "rd/c0d10p",0x3050 }, + { "rd/c0d11p",0x3058 }, + { "rd/c0d12p",0x3060 }, + { "rd/c0d13p",0x3068 }, + { "rd/c0d14p",0x3070 }, + { "rd/c0d15p",0x3078 }, +#endif { "fd", 0x0200 }, { "xda", 0x0d00 }, { "xdb", 0x0d40 }, diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/panic.c linux/kernel/panic.c --- linux.vanilla/kernel/panic.c Sun Jun 21 18:40:56 1998 +++ linux/kernel/panic.c Sun Dec 6 03:32:15 1998 @@ -18,6 +18,7 @@ asmlinkage void sys_sync(void); /* it's really int */ extern void hard_reset_now(void); extern void do_unblank_screen(void); +extern void DAC960_Finalize(void); extern void gdth_halt(void); extern int C_A_D; @@ -55,6 +56,9 @@ printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout); for(i = 0; i < (panic_timeout*1000); i++) udelay(1000); +#ifdef CONFIG_BLK_DEV_DAC960 + DAC960_Finalize(); +#endif #ifdef CONFIG_SCSI_GDTH gdth_halt(); #endif diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/kernel/sys.c linux/kernel/sys.c --- linux.vanilla/kernel/sys.c Sun Dec 6 00:14:47 1998 +++ linux/kernel/sys.c Sun Dec 6 03:32:15 1998 @@ -34,6 +34,7 @@ int C_A_D = 1; extern void adjust_clock(void); +extern void DAC960_Finalize(void); extern void gdth_halt(void); asmlinkage int sys_ni_syscall(void) @@ -188,6 +189,9 @@ if (magic != 0xfee1dead || magic_too != 672274793) return -EINVAL; if (flag == 0x01234567) { +#ifdef CONFIG_BLK_DEV_DAC960 + DAC960_Finalize(); +#endif #ifdef CONFIG_SCSI_GDTH gdth_halt(); #endif @@ -197,6 +201,9 @@ else if (!flag) C_A_D = 0; else if (flag == 0xCDEF0123) { +#ifdef CONFIG_BLK_DEV_DAC960 + DAC960_Finalize(); +#endif #ifdef CONFIG_SCSI_GDTH gdth_halt(); #endif diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/bridge/br.c linux/net/bridge/br.c --- linux.vanilla/net/bridge/br.c Sun Dec 6 00:14:47 1998 +++ linux/net/bridge/br.c Sun Dec 6 03:24:25 1998 @@ -1301,9 +1301,16 @@ /* * Sending */ - if (f->port!=port && port_info[f->port].state == Forwarding) { + + /* + * Vova Oksman: There was the BUG, we must to check timer + * before comparing source and destination ports, becouse + * case that destination was switched from same port with + * source to other port. + */ /* has entry expired? */ - if (f->timer + fdb_aging_time < CURRENT_TIME) { + if (port_info[f->port].state == Forwarding && + f->timer + fdb_aging_time < CURRENT_TIME) { /* timer expired, invalidate entry */ f->flags &= ~FDB_ENT_VALID; if (br_stats.flags & BR_DEBUG) @@ -1314,6 +1321,7 @@ br_flood(skb, port); return(br_dev_drop(skb)); } + if (f->port!=port && port_info[f->port].state == Forwarding) { /* mark that's we've been here... */ skb->pkt_bridged = IS_BRIDGED; diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/bridge/br_tree.c linux/net/bridge/br_tree.c --- linux.vanilla/net/bridge/br_tree.c Sun Jun 21 18:41:03 1998 +++ linux/net/bridge/br_tree.c Sun Dec 6 03:24:25 1998 @@ -215,6 +215,11 @@ break; *stack_ptr++ = nodeplace; stack_count++; if (addr_cmp(new_node->ula, node->ula) == 0) { /* update */ + /* + * Vova Oksman: There was the BUG, in case that port + * was changed we must to update it. + */ + node->port = new_node->port; node->flags = new_node->flags; node->timer = new_node->timer; return(0); diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/Config.in linux/net/ipv4/Config.in --- linux.vanilla/net/ipv4/Config.in Sun Jun 21 18:41:02 1998 +++ linux/net/ipv4/Config.in Sun Dec 6 03:26:46 1998 @@ -16,6 +16,10 @@ comment 'Protocol-specific masquerading support will be built as modules.' if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'IP: ipautofw masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW + bool 'IP: MS PPTP client masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_PPTP + if [ "$CONFIG_IP_MASQUERADE_PPTP" = "y" ]; then + bool 'IP: MS PPTP masq debugging' DEBUG_IP_MASQUERADE_PPTP + fi fi bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP fi diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/igmp.c linux/net/ipv4/igmp.c --- linux.vanilla/net/ipv4/igmp.c Sun Jun 21 18:41:02 1998 +++ linux/net/ipv4/igmp.c Fri Dec 11 20:42:33 1998 @@ -254,8 +254,12 @@ if(skb==NULL) return; - tmp=ip_build_header(skb, dev->pa_addr, address, &dev, IPPROTO_IGMP, NULL, - 28 , 0, 1, NULL); + if (type != IGMP_HOST_LEAVE_MESSAGE) + tmp=ip_build_header(skb, dev->pa_addr, address, &dev, IPPROTO_IGMP, NULL, + 28 , 0, 1, NULL); + else + tmp=ip_build_header(skb, dev->pa_addr, IGMP_ALL_ROUTER, &dev, IPPROTO_IGMP, NULL, + 28, 0, 1, NULL); if(tmp<0) { kfree_skb(skb, FREE_WRITE); @@ -429,7 +433,8 @@ del_timer(&im->timer); /* It seems we have to send Leave Messages to 224.0.0.2 and not to the group itself, to remain RFC 2236 compliant... (jmel) */ - igmp_send_report(im->interface, IGMP_ALL_ROUTER, IGMP_HOST_LEAVE_MESSAGE); + /*igmp_send_report(im->interface, IGMP_ALL_ROUTER, IGMP_HOST_LEAVE_MESSAGE);*/ + igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE); ip_mc_filter_del(im->interface, im->multiaddr); } diff -u --new-file --recursive --exclude-from ../exclude linux.vanilla/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c --- linux.vanilla/net/ipv4/ip_masq.c Sun Dec 6 00:14:48 1998 +++ linux/net/ipv4/ip_masq.c Sun Dec 6 03:26:51 1998 @@ -45,6 +45,49 @@ #define IP_MASQ_TAB_SIZE 256 /* must be power of 2 */ + +/* + * to turn it on, of course +*/ +#define CONFIG_IP_MASQUERADE_PPTP 1 +/* +*/ + + +#ifdef CONFIG_IP_MASQUERADE_PPTP + +/* + * try these if you're having problems... +#define DEBUG_IP_MASQUERADE_PPTP 1 +#define DEBUG_IP_MASQUERADE_VERBOSE 1 +*/ + +/* + * Define this here rather than in /usr/src/linux/include/wherever/whatever.h + * in order to localize my mistakes to one file... + */ + +struct grehdr { + __u8 + recur:3, + is_strict:1, + has_seq:1, + has_key:1, + has_routing:1, + has_cksum:1; + __u8 + version:3, + flags:5; + __u16 + protocol; +}; + + +#define IPPROTO_GRE 47 /* GRE Encapsulation used by Microsoft PPTP */ +static const char *strGREProt = "GRE"; + +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + /* * Implement IP packet masquerading */ @@ -53,6 +96,9 @@ /* * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP + * + * No, I am NOT going to add GRE support to everything that relies on this... + * */ static int masq_proto_num(unsigned proto) @@ -61,6 +107,9 @@ { case IPPROTO_UDP: return (0); break; case IPPROTO_TCP: return (1); break; +#ifdef CONFIG_IP_MASQUERADE_PPTP + case IPPROTO_GRE: return (1); break; /* steal a few TCP ports */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ case IPPROTO_ICMP: return (2); break; default: return (-1); break; } @@ -96,6 +145,19 @@ static __inline__ const char *masq_proto_name(unsigned proto) { +#ifdef CONFIG_IP_MASQUERADE_PPTP + /* + * Yes, I know this sucks for efficiency and is ugly. + * I don't want to track down everything that + * relies on masq_proto_num() and make it GRE-tolerant. + * The number of sites running this patch will probably be small, + * and hopefully the 2.1/2.2 kernel will support masquerading of + * arbitrary IP protocols (or at least GRE). + */ + if (proto == IPPROTO_GRE) { + return strGREProt; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ return strProt[masq_proto_num(proto)]; } @@ -281,6 +343,15 @@ ms->m_link = ip_masq_m_tab[hash]; ip_masq_m_tab[hash] = ms; +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("MASQ: hashed %d %08X:%04hX->%08X:%04hX into in\n", + ms->protocol, + ms->saddr, + ms->sport, + ms->maddr, + ms->mport); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + /* * Hash by proto,s{addr,port} */ @@ -288,6 +359,14 @@ ms->s_link = ip_masq_s_tab[hash]; ip_masq_s_tab[hash] = ms; +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("MASQ: hashed %d %08X:%04hX->%08X:%04hX into out\n", + ms->protocol, + ms->saddr, + ms->sport, + ms->daddr, + ms->dport); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ ms->flags |= IP_MASQ_F_HASHED; return 1; @@ -356,6 +435,15 @@ d_addr = iph->daddr; d_port = portptr[1]; +#ifdef CONFIG_IP_MASQUERADE_PPTP +/* + * Allow for GRE portlessness... + */ + if (protocol == IPPROTO_GRE) { + s_port = d_port = 0; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + return ip_masq_in_get_2(protocol, s_addr, s_port, d_addr, d_port); } @@ -400,6 +488,25 @@ return ms; } } + +#ifdef CONFIG_IP_MASQUERADE_PPTP + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { + if (protocol==ms->protocol && ms->sport == 0 && + s_addr==ms->daddr && d_addr==ms->maddr) { +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + return ms; + } + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX fail\n", protocol, @@ -431,6 +538,15 @@ d_addr = iph->daddr; d_port = portptr[1]; +#ifdef CONFIG_IP_MASQUERADE_PPTP +/* + * Allow for GRE portlessness... + */ + if (protocol == IPPROTO_GRE) { + s_port = d_port = 0; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + return ip_masq_out_get_2(protocol, s_addr, s_port, d_addr, d_port); } @@ -662,6 +778,12 @@ restore_flags(flags); +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (proto == IPPROTO_GRE) { + ms->mport = 0; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + /* * lookup to find out if this port is used. */ @@ -725,6 +847,242 @@ uh->check=0xFFFF; } + +#ifdef CONFIG_IP_MASQUERADE_PPTP +/* + * Quick-and-dirty handling of GRE connections + * to support masquerading a Microsoft PPTP client. + * John Hardin gets all blame... + */ + +/* + * Handle outbound GRE packets. + * + * This is largely a copy of ip_fw_masquerade(), unless I decide + * to fold it in *there* rather than duplicating code *here*... + * + * GRE doesn't have ports, so we use dummy ports 0/0 and only support + * one GRE session per remote server. + */ + +int ip_fw_masq_gre(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct grehdr *greh; +#ifdef DEBUG_IP_MASQUERADE_PPTP + __u8 *greraw; +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + struct ip_masq *ms; + unsigned long flags; + + + greh = (struct grehdr *)&(((char *)iph)[iph->ihl*4]); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + + printk("Outbound GRE packet from %lX to %lX\n", ntohl(iph->saddr), ntohl(iph->daddr)); + + greraw = (__u8 *) greh; + printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n", + greraw[0], + greraw[1], + greraw[2], + greraw[3], + greraw[4], + greraw[5], + greraw[6], + greraw[7], + greraw[8], + greraw[9], + greraw[10], + greraw[11]); + printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n", + greh->has_cksum, + greh->has_routing, + greh->has_key, + greh->has_seq, + greh->is_strict, + greh->recur); + printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version); + printk("GRE proto: %X.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + + if (ntohs(greh->protocol) != 0x880b) { +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + return -1; + } + + /* + * Now hunt the list to see if we have an old entry + */ + + ms = ip_masq_out_get(iph); + if (ms!=NULL) { + ip_masq_set_expire(ms,0); + + /* + * Until we tie into the PPTP control channel, + * there's no way to detect the data channel going + * down. Therefore, we may have an invalid GRE masq + * from a previous session if, for example, the PPP + * link goes down and is brought right back up and the + * PPTP link tries to be established before the masq + * entry expires (see the timer below). + * Make sure that the masq IP address is correct + * for the current session... + */ + if (ms->maddr != dev->pa_addr) { +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("GRE masq IP changed from %lX to %lX\n", + ntohl(ms->maddr),ntohl(dev->pa_addr)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + save_flags(flags); + cli(); + ip_masq_unhash(ms); + ms->maddr = dev->pa_addr; + ip_masq_hash(ms); + restore_flags(flags); + } + } + + /* + * Nope, not found, create a new entry for it + */ + + if (ms==NULL) + { + ms = ip_masq_new_enh(dev, iph->protocol, + iph->saddr, 0, + iph->daddr, 0, + 0, + 0); + if (ms == NULL) + return -1; + } + + /* + * Change the fragments origin + */ + + /* + * Set iph addr from ip_masq obj. + */ + iph->saddr = ms->maddr; + + + /* + * set timeout and check IP header + */ + + ip_masq_set_expire(ms, 6000); /* 60 second timeout */ + ip_send_check(iph); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("GRE O-routed from %lX over %s\n",ntohl(ms->maddr),dev->name); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + + return 0; +} + +/* + * Handle inbound GRE packets. + * + * NT PPTP uses a plain TCP connection to port 1723 to set up the + * GRE encapsulated channel. To figure out where to send the + * inbound GRE packet, we'll look for the masq record for that. + * This means that if you're not masquerading the outbound control + * session, the inbound data session will fail. + * + * To keep this simple, we'll only support one inbound GRE connection + * per PPTP server. + */ + +int ip_fw_demasq_gre(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct grehdr *greh; +#ifdef DEBUG_IP_MASQUERADE_PPTP + __u8 *greraw; +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + struct ip_masq *ms; + + + greh = (struct grehdr *)&(((char *)iph)[iph->ihl*4]); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + + printk("Inbound GRE packet from %lX to %lX\n", ntohl(iph->saddr), ntohl(iph->daddr)); + + greraw = (__u8 *) greh; + printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n", + greraw[0], + greraw[1], + greraw[2], + greraw[3], + greraw[4], + greraw[5], + greraw[6], + greraw[7], + greraw[8], + greraw[9], + greraw[10], + greraw[11]); + printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n", + greh->has_cksum, + greh->has_routing, + greh->has_key, + greh->has_seq, + greh->is_strict, + greh->recur); + printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version); + printk("GRE proto: %X.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + + if (ntohs(greh->protocol) != 0x880b) { +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + return -1; + } + + /* + * reroute to original host if found... + */ + + ms = ip_masq_in_get(iph); + + if (ms != NULL) + { + /* Stop the timer ticking.... */ + ip_masq_set_expire(ms,0); + + iph->daddr = ms->saddr; + + + /* + * Yug! adjust IP checksums, also update + * timeouts. + */ + ip_masq_set_expire(ms, 6000); /* 60 second timeout */ + ip_send_check(iph); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk("GRE I-routed to %lX\n",ntohl(iph->daddr)); +#endif + return 1; + } + + /* sorry, all this trouble for a no-hit :) */ + return 0; +} + +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev) { struct sk_buff *skb=*skb_ptr; @@ -742,6 +1100,10 @@ if (iph->protocol==IPPROTO_ICMP) return (ip_fw_masq_icmp(skb_ptr,dev)); +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (iph->protocol==IPPROTO_GRE) + return (ip_fw_masq_gre(skb_ptr,dev)); +#endif /* CONFIG_IP_MASQUERADE_PPTP */ if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) return -1; @@ -1368,6 +1730,10 @@ switch (iph->protocol) { case IPPROTO_ICMP: return(ip_fw_demasq_icmp(skb_p, dev)); +#ifdef CONFIG_IP_MASQUERADE_PPTP + case IPPROTO_GRE: + return(ip_fw_demasq_gre(skb_p, dev)); +#endif /* CONFIG_IP_MASQUERADE_PPTP */ case IPPROTO_TCP: case IPPROTO_UDP: /* Make sure packet is in the masq range */