diff -u --recursive --new-file v2.0.33/linux/CREDITS linux/CREDITS --- v2.0.33/linux/CREDITS Tue Dec 2 13:52:30 1997 +++ linux/CREDITS Wed Jun 3 15:17:46 1998 @@ -35,6 +35,15 @@ S: San Jose, California 95129 S: USA +N: Andrea Arcangeli +E: arcangeli@mbox.queen.it +W: http://www.cs.unibo.it/~arcangel/ +P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5 +D: Fixed a 2.0.33 mm bug that corrupts memory in linux/mm/vmalloc.c +S: Via Ciaclini 26 +S: Imola 40026 +S: Italy + N: Derek Atkins E: warlord@MIT.EDU D: Linux-AFS Port, random kernel hacker, @@ -81,6 +90,14 @@ S: Notre Dame, Indiana S: USA +N: James Banks +E: james.banks@caldera.com +D: TLAN network driver +S: Caldera, Inc. +S: 633 South 550 East +S: Provo, UT 84606 +S: USA + N: Peter Bauer E: 100136.3530@compuserve.com D: Driver for depca-ethernet-board @@ -89,10 +106,12 @@ S: Germany N: Fred Baumgarten -E: dc6iq@insu1.etec.uni-karlsruhe.de +E: dc6iq@insl1.etec.uni-karlsruhe.de +E: dc6iq@adacom.org +E: dc6iq@db0ais.#hes.deu.eu (packet radio) D: NET-2 & netstat(8) -S: Kandelstrasse 27 -S: 76297 Stutensee +S: Soevener Strasse 11 +S: 53773 Hennef S: Germany N: Donald Becker @@ -207,7 +226,8 @@ N: Gordon Chaffee E: chaffee@plateau.cs.berkeley.edu -D: vfat filesystem +W: http://bmrc.berkeley.edu/people/chaffee/ +D: vfat, fat32, joliet, native language support S: 3674 Oakwood Terrace #201 S: Fremont, California 94536 S: USA @@ -401,11 +421,12 @@ S: Germany N: Jeremy Fitzhardinge -E: jeremy@sw.oz.au +E: jeremy@zip.com.au D: Improved mmap and munmap handling D: General mm minor tidyups -S: 99 Albermarle Street -S: Newtown 2042 +S: 67 Surrey St. +S: Darlinghurst, Sydney +S: NSW 2010 S: Australia N: Ralf Flaxa @@ -477,11 +498,11 @@ D: Kernel / timekeeping stuff N: Dmitry S. Gorodchanin -E: begemot@bgm.rosprint.net +E: pgmdsg@ibi.com D: RISCom/8 driver, misc kernel fixes. -S: 6/1 M.Koneva bl, apt #125 -S: Poltava 314023 -S: Ukraine +S: 4 Main Street +S: Woodbridge, Connecticut 06525 +S: USA N: Paul Gortmaker E: gpg109@rsphy1.anu.edu.au @@ -617,6 +638,7 @@ N: Ron Holt E: ron@caldera.com W: http://www.holt.org/ +P: 1024/1FD44539 DF 4B EB 9F 5B 68 38 9A 40 E3 FB 71 D1 C8 0B 56 D: Kernel development D: Minor kernel modifications to support Wabi and Wine S: Caldera, Inc. @@ -680,7 +702,7 @@ S: USA N: Bernhard Kaindl -E: bkaindl@netway.at +E: bernhard.kaindl@gmx.net E: edv@bartelt.via.at D: Author of a menu based configuration tool, kmenu, which D: is the predecessor of 'make menuconfig' and 'make xconfig'. @@ -714,9 +736,11 @@ N: Ian Kluft E: ikluft@thunder.sbay.org -D: Smail binary packages for Slackware and Debian -S: 2200 Monroe Street #1509 -S: Santa Clara, California 95050-3452 +W: http://www.kluft.com/~ikluft/ +D: NET-1 beta testing & minor patches, original Smail binary packages for +D: Slackware and Debian, vote-taker for 2nd comp.os.linux reorganization +S: PO Box 611311 +S: San Jose, CA 95161-1311 S: USA N: Alain L. Knaff @@ -764,8 +788,8 @@ N: Bas Laarhoven E: bas@vimec.nl D: Loadable modules and ftape driver -S: Mr. v. Boemellaan 39 -S: NL-5237 KA 's-Hertogenbosch +S: J. Obrechtstr 23 +S: NL-5216 GP 's-Hertogenbosch S: The Netherlands N: Savio Lam @@ -806,6 +830,14 @@ E: loewis@informatik.hu-berlin.de D: script binary format +N: Jamie Lokier +E: jamie@imbolc.ucc.ie +D: Reboot-through-BIOS for broken 486 motherboards +S: 26 Oatlands Road +S: Oxford +S: OX2 0ET +S: United Kingdom + N: Mark Lord E: mlord@pobox.com D: Author of IDE driver (ide.c), hd.c support @@ -914,8 +946,8 @@ N: Dirk Melchers E: dirk@merlin.nbg.sub.org D: 8 bit XT hard disk driver for OMTI5520 -S: Heidackerstrass 19 -S: D-91056 Erlangen +S: Schloessleinsgasse 31 +S: D-90453 Nuernberg S: Germany N: Michael Meskes @@ -968,6 +1000,7 @@ N: Rick Miller E: rdmiller@execpc.com +W: http://www.execpc.com/~rdmiller/ D: Original Linux Device Registrar (Major/minor numbers) D: au-play, bwBASIC S: S78 W16203 Woods Road @@ -1101,8 +1134,12 @@ D: Some PCI kernel support N: Stefan Probst -E: snprobst@immd4.informatik.uni-erlangen.de -D: The Linux Support Team Erlangen +E: sp@caldera.de +D: The Linux Support Team Erlangen, 1993-97 +S: Caldera (Deutschland) GmbH +S: Lazarettstrasse 8 +S: 91054 Erlangen +S: Germany N: Daniel Quinlan E: quinlan@pathname.com @@ -1427,6 +1464,14 @@ S: Obere Heerbergstrasse 17 S: 97078 Wuerzburg S: Germany + +N: Greg Ungerer +E: gerg@stallion.com +D: Author of Stallion multiport serial drivers +S: Stallion Technologies +S: 33 Woodstock Rd +S: Toowong, QLD. 4066 +S: Australia N: Jeffrey A. Uphoff E: juphoff@nrao.edu diff -u --recursive --new-file v2.0.33/linux/Documentation/Changes linux/Documentation/Changes --- v2.0.33/linux/Documentation/Changes Mon Aug 18 20:16:26 1997 +++ linux/Documentation/Changes Wed Jun 3 15:17:46 1998 @@ -40,11 +40,21 @@ Voyez le site http://www.linux-kheops.com/traduc/kernels/ pour la traduction francaise (merci, David Bourgin). (French translation) -Last updated: August 14, 1997. -Current Author: Chris Ricker (gt1355b@prism.gatech.edu). + Per quelli che preferiscono avere una versione in Italiano di questo +documento, si consulti la traduzione di Giovanni Bortolozzo reperibile a +http://www.pluto.linux.it/ildp/kernel/Changes-2.0.html (Italian +translation). -Current Releases -**************** + The most current version should always be available from +http://cyberbuzz.gatech.edu/kaboom/linux/ as well. + +Last updated: February 16, 1998. +Current Author: Chris Ricker (kaboom@gatech.edu). + +Current Releases (the latest necessary for basic functionality, not +******************************************************************* + + necessarily the latest and greatest) - Kernel modules 2.0.0 - PPP daemon 2.2.0f @@ -372,6 +382,12 @@ masquerading. You'll need to get ftp://ftp.xos.nl/pub/linux/ipfwadm/ipfwadm-2.3.0.tar.gz to use this. + To enable IP forwarding, you may need to + + echo 1 > /proc/sys/net/ipv4/ip_forwarding + + as well. + ISDN support ============ @@ -587,6 +603,14 @@ you'll need to get the latest version, currently available at ftp://tsx-11.mit.edu/pub/linux/packages/ext2fs/e2fsprogs-1.06.tar.gz. +FlagShip and /dev/full +====================== + + The behavior of /dev/full changed with kernel 2.0.31. In addition to +improving system security, the change broke executables created by the +FlagShip database compiler. Fixes can be found at http://www.fship.com +via the "What's New" or "Support" buttons. + How to know the version of the installed programs ************************************************* @@ -725,8 +749,7 @@ Please send info about any other packages that 2.0.x "broke" or about any new features of 2.0.x that require extra or new packages for use to -Chris Ricker (gt1355b@prism.gatech.edu). I generate this from a -modified texinfo setup, so you don't need to bother generating a diff -against the current version before you send the additional information -to me. +Chris Ricker . I generate this from a modified +texinfo setup, so you don't need to bother generating a diff against +the current version before you send the additional information to me. diff -u --recursive --new-file v2.0.33/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.0.33/linux/Documentation/Configure.help Tue Mar 10 13:19:08 1998 +++ linux/Documentation/Configure.help Wed Jun 3 15:17:46 1998 @@ -1,4 +1,4 @@ -# Maintained by Axel Boldt (boldt@math.ucsb.edu) +# Maintained by Axel Boldt (boldt@math.ucsb.edu) # # This version of the Linux kernel configuration help texts # corresponds to the kernel versions 2.0.x. @@ -6,16 +6,16 @@ # International versions of this file available on the WWW: # - http://jf.gee.kyoto-u.ac.jp/JF/JF-ftp/euc/Configure.help.euc # is a Japanese translation, maintained by Tetsuyasu YAMADA -# (tetsu@cauchy.nslab.ntt.jp). -# - http://nevod.perm.su/service/linux/doc/kernel/Configure.help +# (tetsu@cauchy.nslab.ntt.jp). +# - http://nevod.perm.su/service/linux/doc/kernel/Configure.help # is a Russian translation, maintained by kaf@linux.nevod.perm.su. # # Information about what a kernel is, what it does, how to patch and # compile it and much more is contained in the Kernel-HOWTO, available # via ftp (user: anonymous) from sunsite.unc.edu in the directory -# /pub/Linux/docs/HOWTO. +# /pub/Linux/docs/HOWTO. # -# Format of this file: descriptionvariablehelptext. +# Format of this file: descriptionvariablehelptext. # If the question being documented is of type "choice", we list # only the first occurring config variable. The help texts # must not contain empty lines. No variable should occur twice; if it @@ -29,7 +29,7 @@ # hypothetical user who has just bought a PC, removed Windows, # installed Linux and is now recompiling the kernel for the first # time. Tell them what to do if they're unsure. Technical information -# should go in a README in the Documentation directory. Mention all +# should go in a README in the Documentation directory. Mention all # the relevant READMEs and HOWTOs in the help text. # # All this was shamelessly stolen from several different sources. Many @@ -39,24 +39,24 @@ Prompt for development and/or incomplete code/drivers CONFIG_EXPERIMENTAL - Some of the various things that Linux supports (such as network - drivers, filesystems, network protocols, etc.) can be in a state - of development where the functionality, stability, or the level of - testing is not yet high enough for general use. This is usually - known as the "alpha-test" phase amongst developers. If a feature is - currently in alpha-test, then the developers usually discourage + Some of the various things that Linux supports (such as network + drivers, filesystems, network protocols, etc.) can be in a state + of development where the functionality, stability, or the level of + testing is not yet high enough for general use. This is usually + known as the "alpha-test" phase amongst developers. If a feature is + currently in alpha-test, then the developers usually discourage uninformed widespread use of this feature by the general public to - avoid "Why doesn't this work?" type mail messages. However, active - testing and use of these systems is welcomed. Just be aware that it + avoid "Why doesn't this work?" type mail messages. However, active + testing and use of these systems is welcomed. Just be aware that it may not meet the normal level of reliability or it may fail to work - in some special cases. Detailed bug reports from people familiar with - the kernel internals are usually welcomed by the developers. + in some special cases. Detailed bug reports from people familiar + with the kernel internals are usually welcomed by the developers. Unless you intend to help test and develop a feature or driver that - falls into this category, or you have a situation that requires using - these features you should probably say N here, which will cause this - configure script to present you with fewer choices. If you say Y here, - you will be offered the choice of using features or drivers that are - currently considered to be in the alpha-test phase. + falls into this category, or you have a situation that requires + using these features you should probably say N here, which will + cause this configure script to present you with fewer choices. If + you say Y here, you will be offered the choice of using features or + drivers that are currently considered to be in the alpha-test phase. Kernel math emulation CONFIG_MATH_EMULATION @@ -81,14 +81,13 @@ Normal floppy disk support CONFIG_BLK_DEV_FD - If you want to use your floppy disk drive(s) under Linux, say - Y. Information about this driver, especially important for IBM - Thinkpad users, is contained in drivers/block/README.fd. This - driver is also available as a module ( = code which can be inserted - in and removed from the running kernel whenever you want). If you - want to compile it as a module, say M here and read - Documentation/modules.txt. - + If you want to use your floppy disk drive(s) under Linux, say Y. + Information about this driver, especially important for IBM Thinkpad + users, is contained in drivers/block/README.fd. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. + RAM disk support CONFIG_BLK_DEV_RAM Enabling this option will allow you to use a portion of your RAM @@ -125,17 +124,17 @@ from the machine to itself. Most users will answer N here. Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support -CONFIG_BLK_DEV_IDE +CONFIG_BLK_DEV_IDE This will use the full-featured IDE driver to control up to four IDE interfaces, for a combination of up to eight IDE disk/cdrom/tape/floppy drives. Useful information about large (>540MB) IDE disks, soundcard IDE ports, and other topics, is all contained in Documentation/ide.txt. If you have one or more IDE - drives, say Y here. If your system has no IDE drives, or if memory - requirements are really tight, you could say N here, and select - the Old harddisk driver instead to save about 13kB of memory in - the kernel. To fine-tune IDE drive/interface parameters for - improved performance, look for the hdparm package at + drives, say Y here. If your system has no IDE drives, or if + memory requirements are really tight, you could say N here, and + select the Old harddisk driver instead to save about 13kB of + memory in the kernel. To fine-tune IDE drive/interface parameters + for improved performance, look for the hdparm package at sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/ Old harddisk (MFM/RLL/IDE) driver @@ -165,7 +164,7 @@ address (0x1f0), along with IDE drives at the secondary/3rd/4th port addresses. Normally, just say N here; you will then use the new driver for all 4 interfaces. - + Include IDE/ATAPI CDROM support CONFIG_BLK_DEV_IDECD If you have a CDROM drive using the ATAPI protocol, say Y. ATAPI is @@ -198,12 +197,11 @@ CONFIG_BLK_DEV_IDEFLOPPY If you have an IDE floppy which uses the ATAPI protocol, say Y. ATAPI is a new protocol used by IDE cdrom/tape/floppy drives, - similar to the SCSI protocol. IDE floppy drives include the - LS-120 and the ATAPI ZIP (ATAPI PD-CD drives are not supported - by this driver; support for PD-CD drives is available through - the SCSI emulation). At boot time, the FLOPPY drive will be - identified along with other IDE devices, as "hdb" or "hdc", or - something similar. + similar to the SCSI protocol. IDE floppy drives include the LS-120 + and the ATAPI ZIP (ATAPI PD-CD drives are not supported by this + driver; support for PD-CD drives is available through the SCSI + emulation). At boot time, the FLOPPY drive will be identified along + with other IDE devices, as "hdb" or "hdc", or something similar. SCSI emulation support CONFIG_BLK_DEV_IDESCSI @@ -315,21 +313,21 @@ XT harddisk support CONFIG_BLK_DEV_XD - Very old 8 bit hard disk controllers used in the IBM XT computer. To - include a driver for these, say Y. If you want to compile the 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. It's pretty unlikely that you have one of - these: say N. + Very old 8 bit hard disk controllers used in the IBM XT computer. + To include a driver for these, say Y. If you want to compile the + 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. + It's pretty unlikely that you have one of these: say N. Multiple devices driver support CONFIG_BLK_DEV_MD This driver lets you combine several harddisk partitions into one - logical block device. Information about how and why to use it and the - necessary tools are available over ftp (user: anonymous) from + logical block device. Information about how and why to use it and + the necessary tools are available over ftp (user: anonymous) from sweet-smoke.ufr-info-p7.ibp.fr/pub/public/Linux in the md package - and the md-FAQ. Please read drivers/block/README.md. If unsure, say - N. + and the md-FAQ. Please read drivers/block/README.md. If unsure, + say N. Linear (append) mode CONFIG_MD_LINEAR @@ -351,16 +349,16 @@ in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. If unsure, say Y. -Support for Deskstation RPC44 +Support for Deskstation RPC44 CONFIG_DESKSTATION_RPC44 This is a machine with a R4400 100 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux - on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://lena.fnet.fr/ (To browse the WWW, you need to - have access to a machine on the Internet that has one of the - programs lynx, netscape or Mosaic). + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + http://lena.fnet.fr/ (To browse the WWW, you need to have access + to a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). -Support for Mips Magnum 3000 +Support for Mips Magnum 3000 CONFIG_MIPS_MAGNUM_3000 To compile a Linux kernel that runs on these, say Y here. For details about Linux on the MIPS architecture, check out the @@ -371,20 +369,20 @@ Support for Mips Magnum 4000 CONFIG_MIPS_MAGNUM_4000 This is a machine with a R4000 100 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux - on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://lena.fnet.fr/ (To browse the WWW, you need to - have access to a machine on the Internet that has one of the - programs lynx, netscape or Mosaic). + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + http://lena.fnet.fr/ (To browse the WWW, you need to have access + to a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). Support for Olivetti M700 CONFIG_OLIVETTI_M700 This is a machine with a R4000 100 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about Linux - on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://lena.fnet.fr/ (To browse the WWW, you need to - have access to a machine on the Internet that has one of the - programs lynx, netscape or Mosaic). + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + http://lena.fnet.fr/ (To browse the WWW, you need to have access + to a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). Support for Deskstation Tyne CONFIG_DESKSTATION_TYNE @@ -398,34 +396,33 @@ Support for Acer PICA 1 chipset CONFIG_ACER_PICA_61 This is a machine with a R4400 134/150 MHz CPU. To compile a Linux - kernel that runs on these, say Y here. For details about - Linux on the MIPS architecture, check out the Linux/MIPS FAQ on the - WWW at http://lena.fnet.fr/ (To browse the WWW, you need to have - access to a machine on the Internet that has one of the programs - lynx, netscape or Mosaic). + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + http://lena.fnet.fr/ (To browse the WWW, you need to have access + to a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). Support for DECstation CONFIG_DECSTATION The DECStation 3100 (with a MIPS R2000 series CPU) and DECStation - 5000/xxx (MIPS R3000 series CPU) are also sometimes labeled - PMAX. They often run the Ultrix operating system. To compile a Linux - kernel that runs on these, say Y here. For details about Linux - on the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at - http://lena.fnet.fr/ (To browse the WWW, you need to - have access to a machine on the Internet that has one of the - programs lynx, netscape or Mosaic). + 5000/xxx (MIPS R3000 series CPU) are also sometimes labeled PMAX. + They often run the Ultrix operating system. To compile a Linux + kernel that runs on these, say Y here. For details about Linux on + the MIPS architecture, check out the Linux/MIPS FAQ on the WWW at + http://lena.fnet.fr/ (To browse the WWW, you need to have access + to a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). CPU type CONFIG_CPU_R3000 - Give the type of your machine's MIPS CPU. For this question, - it suffices to give a unique prefix of the option you want to - choose. + Give the type of your machine's MIPS CPU. For this question, it + suffices to give a unique prefix of the option you want to choose. Networking support CONFIG_NET - Unless you really know what you are doing, you should say Y - here. The reason is that some programs need it even if you configure - a stand-alone machine that won't be connected to any other computer. + Unless you really know what you are doing, you should say Y here. + The reason is that some programs need it even if you configure a + stand-alone machine that won't be connected to any other computer. from an older kernel, you should consider updating your networking tools too; read net/README for details. @@ -445,8 +442,8 @@ CONFIG_FIREWALL A firewall is a computer which protects a local network from the rest of the World: all traffic to and from computers on the local - net is inspected by the firewall first. If you want to configure - your Linux box as a firewall for a local network, say Y here. If + net is inspected by the firewall first. If you want to configure + your Linux box as a firewall for a local network, say Y here. If your local network is TCP/IP based, you will have to say Y to "IP: firewalling", below. You also need to say Y here and enable "IP firewalling" below in order to be able to use IP masquerading @@ -456,43 +453,21 @@ need to allocate valid IP host addresses for the machines on the local net) or to use the ip packet accounting to see what is using all your network bandwidth. Chances are that you should use this on - any machine being run as a router and not on a host. If unsure, say - N. + any machine being run as a router and not on a host. + If unsure, say N. SYN flood protection CONFIG_SYN_COOKIES Normal TCP/IP networking is open to an attack known as SYN flooding. This attack prevents legitimate users from being able to connect to your computer and requires very little work for the attacker. - SYN cookies provide protection against this type of attack. With + SYN cookies provide protection against this type of attack. With this option turned on the TCP/IP stack will use a cryptographic challenge protocol known as SYN cookies to enable legitimate users to continue to connect, even when your machine is under attack. - The RST_COOKIES option provides an alternative method to accomplish - the same end. SYN cookies use less space than RST cookies, - but have a small probability of introducing an non timed-out - failure to connect in the remote TCP. You can use both options - simultatenously. If you are SYN flooded, the source address - reported by the kernel is likely to have been forged by the attacker. - The source address is reported as an aid in tracing the packets to - their actual source. - -SYN flood protection -CONFIG_RST_COOKIES - Normal TCP/IP networking is open to an attack known as SYN flooding. - This attack prevents legitimate users from being able to connect to - your computer and requires very little work for the attacker. - SYN cookies provide protection against this type of attack. With - this option turned on the TCP/IP stack will use a cryptographic - challenge protocol known as RST cookies to enable legitimate users - to continue to connect, even when your machine is under attack. - The SYN_COOKIES option provides an alternative method to accomplish - the same end. RST cookies use more space than SYN cookies on your - machine, but never increase the probability of a frozen connection - in a remote TCP. You can use both options simultatenously. If you - are SYN flooded, the source address reported by the kernel is likely - to have been forged by the attacker. The source address is reported - as an aid in tracing the packets to their actual source. + If you are SYN flooded, the source address reported by the kernel is + likely to have been forged by the attacker. The source address is + reported as an aid in tracing the packets to their actual source. Sun floppy controller support CONFIG_BLK_DEV_SUNFD @@ -544,17 +519,16 @@ the kernel to be able to use the memory above 64MB, pass the command line option "mem=XXXM" (where XXX is the memory size in megabytes) to your kernel during boot time. See the documentation of your boot - loader (lilo or loadlin) about how to pass options to the - kernel. The lilo procedure is also explained in the SCSI-HOWTO, - available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. You also need at least 512kB - of RAM cache if you have more than 64MB of RAM. Some other things - to try when experiencing seemingly random, "weird" problems: 1) - passing the "no-hlt" option to the kernel 2) passing the "no-387" - option to the kernel 3) passing the "mem=4M" option to the kernel - (thereby disabling all but the first 4M of RAM) 4) disabling the - cache from your BIOS settings 5) exchanging RAM chips 6) exchanging - the motherboard. + loader (lilo or loadlin) about how to pass options to the kernel. + The lilo procedure is also explained in the SCSI-HOWTO, available + via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + You also need at least 512kB of RAM cache if you have more than 64MB + of RAM. Some other things to try when experiencing seemingly random, + "weird" problems: 1) passing the "no-hlt" option to the kernel + 2) passing the "no-387" option to the kernel 3) passing the "mem=4M" + option to the kernel (thereby disabling all but the first 4M of RAM) + 4) disabling the cache from your BIOS settings + 5) exchanging RAM chips 6) exchanging the motherboard. Using SRM as bootloader CONFIG_ALPHA_SRM @@ -607,7 +581,7 @@ about which PCI hardware does work under Linux and which doesn't. If some of your PCI devices don't work and you get a warning during boot time ("man dmesg"), please follow the instructions at the top - of include/linux/pci.h. + of include/linux/pci.h. PCI bridge optimization (experimental) CONFIG_PCI_OPTIMIZE @@ -622,24 +596,24 @@ i82371SB or i82371AB), you will want to enable this option to allow use of bus-mastering DMA data transfers. Read the comments at the beginning of drivers/block/triton.c and Documentation/ide.txt. - Check the file Documentation/Changes for location and latest version of - the hdparm utility. It is safe to say Y to this question. + Check the file Documentation/Changes for location and latest version + of the hdparm utility. It is safe to say Y to this question. System V IPC CONFIG_SYSVIPC - Inter Process Communication is a suite of library functions and system - calls which let processes (= running programs) synchronize and - exchange information. It is generally considered to be a good thing, - and some programs won't run unless you enable this. In particular, - if you want to run the DOS emulator dosemu under Linux (read the - DOSEMU-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO), you'll need to say Y here. You - can find documentation about IPC in ipc.info, which is contained in - sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (extract with "tar - xzvf filename"). These docs are in the info format which is used to - document GNU software and can be read from within emacs ("Ctrl-h i") - or with the program info ("man info"). Enabling this option enlarges - your kernel by about 7kB. Just say Y. + Inter Process Communication is a suite of library functions and + system calls which let processes (= running programs) synchronize + and exchange information. It is generally considered to be a good + thing, and some programs won't run unless you enable this. In + particular, if you want to run the DOS emulator dosemu under Linux + (read the DOSEMU-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO), you'll need to say Y here. + You can find documentation about IPC in ipc.info, which is contained + in sunsite.unc.edu:/pub/Linux/docs/man/info.tar.gz (extract with + "tar xzvf filename"). These docs are in the info format which is + used to document GNU software and can be read from within emacs + ("Ctrl-h i") or with the program info ("man info"). Enabling this + option enlarges your kernel by about 7kB. Just say Y. Kernel support for ELF binaries CONFIG_BINFMT_ELF @@ -669,7 +643,7 @@ CONFIG_KERNEL_ELF The gcc version 2.7.0 and newer produces the new ELF binary format as default. If you have such a compiler (try "gcc -v"), say Y here, - otherwise N. + otherwise N. It is possible, albeit almost pointless, to compile the kernel in a.out (i.e. QMAGIC) format even if your compiler produces ELF as default. For that, you would have to say N here and change the @@ -691,7 +665,7 @@ wish to ensure that absolutely none of your programs will use this older executable format. If you don't know what to answer at this point then answer Y. If someone told you "You need a kernel with - QMAGIC support" then you'll have to say Y here. You may answer M + QMAGIC support" then you'll have to say Y here. You may answer M to compile a.out support as a module and later load the module when you want to use a program or library in a.out format. Saying M or N here is dangerous though, because some crucial programs on your @@ -717,8 +691,8 @@ warrant removing support. However its removal is a good idea if you do not have the JDK installed. If you don't know what to answer at this point then answer Y. You may answer M for module support and - later load the module when you install the JDK or find an interesting - Java program that you can't live without. + later load the module when you install the JDK or find an + interesting Java program that you can't live without. Processor type CONFIG_M386 @@ -733,7 +707,7 @@ say "386" or "486" here even if running on a Pentium or PPro machine. If you don't know what to do, say "386". -Compile the kernel into the ELF object format +Compile the kernel into the ELF object format CONFIG_ELF_KERNEL ELF (Executable and Linkable Format) is a format for libraries and executables used across different architectures and operating @@ -756,27 +730,27 @@ Enable loadable module support CONFIG_MODULES - Kernel modules are small pieces of compiled code which can be + Kernel modules are small pieces of compiled code which can be inserted in or removed from the running kernel, using the - programs insmod and rmmod. This is described in the file - Documentation/modules.txt. Modules can be device drivers, file - systems, binary executable formats, and so on. If you think that - you may want to make use of modules with this kernel in the future, + programs insmod and rmmod. This is described in the file + Documentation/modules.txt. Modules can be device drivers, file + systems, binary executable formats, and so on. If you think that + you may want to make use of modules with this kernel in the future, then say Y here. If unsure, say Y. Set version information on all symbols for modules CONFIG_MODVERSIONS Usually, modules have to be recompiled whenever you switch to a new - kernel. Enabling this option makes it possible, and safe, to use the - same modules even after compiling a new kernel; this requires the - program modprobe. All the software needed for module support is in - the modules package (check the file Documentation/Changes for + kernel. Enabling this option makes it possible, and safe, to use + the same modules even after compiling a new kernel; this requires + the program modprobe. All the software needed for module support is + in the modules package (check the file Documentation/Changes for location and latest version). NOTE: if you say Y here but don't have the program genksyms (which is also contained in the above mentioned modules package), then the building of your kernel will fail. If you are going to use modules that are generated from non-kernel sources, you would benefit from this option. Otherwise - it's not that important. So, N ought to be a safe bet. + it's not that important. So, N ought to be a safe bet. Kernel daemon support CONFIG_KERNELD @@ -796,7 +770,7 @@ ARP daemon support (EXPERIMENTAL) CONFIG_ARPD - Normally, the kernel maintains an internal cache which maps IP + Normally, the kernel maintains an internal cache which maps IP addresses to hardware addresses on the local network, so that Ethernet/Token Ring/ etc. frames are sent to the proper address on the physical networking layer. For small networks having a few @@ -824,8 +798,7 @@ program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected Unix computer. Read the Term-HOWTO, available via ftp (user: anonymous) - on sunsite.unc.edu:/pub/Linux/docs/HOWTO). Short answer: - say Y. + on sunsite.unc.edu:/pub/Linux/docs/HOWTO). Short answer: say Y. IP: forwarding/gatewaying CONFIG_IP_FORWARD @@ -890,22 +863,21 @@ If you want to configure your Linux box as a firewall for a local TCP/IP based network, say Y here. This will enlarge your kernel by about 2kB. You may need to read the FIREWALL-HOWTO, available via - ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, you will need the - ipfwadm tool (check the file Documentation/Changes for location and - latest version) to allow selective blocking of internet traffic - based on type, origin and destination. You need to enable IP - firewalling in order to be able to use IP masquerading (i.e. local - computers can chat with an outside host, but that outside host is - made to think that it is talking to the firewall box. Makes the - local network completely invisible and avoids the need to allocate - valid IP host addresses for the machines on the local net) or to use - the IP packet accounting to see what is using all your network - bandwidth. This option is also needed when you want to enable the - transparent proxying support (makes the computers on the local - network think they're talking to a remote computer, while in reality - the traffic is redirected by your Linux firewall to a local proxy - server). + ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + Also, you will need the ipfwadm tool (check the file + Documentation/Changes for location and latest version) to allow + selective blocking of internet traffic based on type, origin and + destination. You need to enable IP firewalling in order to be able + to use IP masquerading (i.e. local computers can chat with an + outside host, but that outside host is made to think that it is + talking to the firewall box. Makes the local network completely + invisible and avoids the need to allocate valid IP host addresses + for the machines on the local net) or to use the IP packet + accounting to see what is using all your network bandwidth. This + option is also needed when you want to enable the transparent + proxying support (makes the computers on the local network think + they're talking to a remote computer, while in reality the traffic + is redirected by your Linux firewall to a local proxy server). IP: accounting CONFIG_IP_ACCT @@ -914,8 +886,8 @@ a router or a firewall for some local network, in which case you naturally should have said Y to IP forwarding/gatewaying resp. IP firewalling. The data is accessible with "cat /proc/net/ip_acct", so - you want to say Y to the /proc filesystem below, if you say Y - here. To specify what exactly should be recorded, you need the tool + you want to say Y to the /proc filesystem below, if you say Y here. + To specify what exactly should be recorded, you need the tool ipfwadm (check the file Documentation/Changes for location and latest version). @@ -934,12 +906,12 @@ in and removed from the running kernel whenever you want), one encapsulator and one decapsulator. You can read details in drivers/net/README.tunnel. Most people can say N. - + IP: firewall packet logging CONFIG_IP_FIREWALL_VERBOSE - This gives you information about what your firewall did with - packets it received. The information is handled by the klogd demon - which is responsible for kernel messages ("man klogd"). + This gives you information about what your firewall did with packets + it received. The information is handled by the klogd demon which is + responsible for kernel messages ("man klogd"). IP: transparent proxying (EXPERIMENTAL) CONFIG_IP_TRANSPARENT_PROXY @@ -969,35 +941,31 @@ Internet using SLiRP [SLiRP is a SLIP/PPP emulator that works if you have a regular dial up shell account on some UNIX computer; get it from ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/].) - Details on how to set things up are contained in the - IP Masquerading FAQ, available at http://www.indyramp.com/masq/ + Details on how to set things up are contained in the IP Masquerading + FAQ, available at http://www.indyramp.com/masq/ To use masquerading you must also enable Network Firewalls, IP forwarding/gatewaying, IP firewalling and (ideally, but optionally) IP always defragment. If you want this, say Y. -IP: ipautofw masquerade support -CONFIG_IP_MASQUERADE_IPAUTOFW (Experimental) - ipautofw is a program by Richard Lynch allowing additional - support for masquerading protocols which do not (as yet) - have additional protocol helpers. - Information and source for ipautofw is available from - ftp://ftp.netis.com/pub/members/rlynch/ - The ipautofw code is still under development and so is currently - marked EXPERIMENTAL. - If you want this, say Y. +IP: ipautofw masquerading (EXPERIMENTAL) +CONFIG_IP_MASQUERADE_IPAUTOFW + Richard Lynch's ipautofw allows masquerading to work with protocols + which do not (as yet) have specific protocol helpers. Its source, + and other information, is available at + ftp://ftp.netis.com/pub/members/rlynch/. IP: ICMP masquerading CONFIG_IP_MASQUERADE_ICMP The basic masquerade code described for CONFIG_IP_MASQUERADE only - handles TCP or UDP packets (and ICMP errors for existing + handles TCP or UDP packets (and ICMP errors for existing connections). This option adds additional support for masquerading ICMP packets, such as ping or the probes used by the Windows 95 tracert program. If you want this, say Y. -IP: always defragment -CONFIG_IP_ALWAYS_DEFRAG +IP: always defragment +CONFIG_IP_ALWAYS_DEFRAG This option means that all incoming fragments (= parts of IP packets that arose when some host between origin and destination decided that the IP packets were too large and cut them in pieces) will be @@ -1006,30 +974,30 @@ have enabled the masquerading support (CONFIG_IP_MASQUERADE), because this facility requires that second and further fragments can be related to TCP or UDP port numbers, which are only stored in the - first fragment. When using IP firewall support - (CONFIG_IP_FIREWALL), you might also want to enable this option, to - have a more reliable firewall (otherwise second and further - fragments will always be accepted by the firewall). When using - transparent proxying (CONFIG_IP_TRANSPARENT_PROXY), this option is - implicit, although it is safe to say Y here. Do not say Y to this - option except when running either a firewall that is the sole link - to your network or a transparent proxy. Never ever say Y to this for - a normal router or host. + first fragment. When using IP firewall support (CONFIG_IP_FIREWALL), + you might also want to enable this option, to have a more reliable + firewall (otherwise second and further fragments will always be + accepted by the firewall). When using transparent proxying + (CONFIG_IP_TRANSPARENT_PROXY), this option is implicit, although it + is safe to say N here. Do not say Y to this option except when + running either a firewall that is the sole link to your network or + a transparent proxy. + Never ever say Y to this for a normal router or host. IP: aliasing support CONFIG_IP_ALIAS Sometimes it is useful to give several addresses to a single network - interface (= serial port or ethernet card). The most common case is + interface (= serial port or ethernet card). The most common case is that you want to serve different WWW documents to the outside according to which of your host names they used to connect to - you. This is explained in detail on the WWW at + you. This is explained in detail on the WWW at http://www.thesphere.com/~dlp/TwoServers/ (to browse the WWW, you need to have access to a machine on the Internet that has one of the - programs lynx, netscape or Mosaic). Another scenario would be that + programs lynx, netscape or Mosaic). Another scenario would be that there are two logical networks living on your local ethernet and you - want to access them both with the same ethernet card. The + want to access them both with the same ethernet card. The configuration of these alias addresses is done with a special name - syntax explained in Documentation/networking/alias.txt. If you want + syntax explained in Documentation/networking/alias.txt. If you want this, say Y. Most people don't need it and say N. IP: multicast routing (EXPERIMENTAL) @@ -1047,9 +1015,10 @@ CONFIG_INET_PCTCP If you have been having difficulties telneting to your Linux machine from a DOS system that uses (broken) PC/TCP networking software (all - versions up to OnNet 2.0) over your local ethernet try enabling this - option. Everyone else says N. People having problems with NCSA telnet - should see the file linux/Documentation/networking/ncsa-telnet. + versions up to OnNet 2.0) over your local ethernet try enabling this + option. Everyone else says N. + People having problems with NCSA telnet should see the file + linux/Documentation/networking/ncsa-telnet. Reverse ARP CONFIG_INET_RARP @@ -1077,55 +1046,57 @@ CONFIG_INET_SNARL Say Y if you are on a subnetted network with all machines connected by Ethernet segments only, as this option optimizes network access - for this special case. If there are other connections, e.g. SLIP - links, between machines of your IP network, say N. If in doubt, say - N. The PATH mtu discovery facility will cover most cases anyway. + for this special case. If there are other connections, e.g. SLIP + links, between machines of your IP network, say N. If in doubt, + say N. The PATH mtu discovery facility will cover most cases anyway. Disable Path MTU Discovery (normally enabled) CONFIG_NO_PATH_MTU_DISCOVERY MTU (maximal transfer unit) is the size of the chunks we send out - over the net. "Path MTU Discovery" means that, instead of always + over the net. "Path MTU Discovery" means that, instead of always sending very small chunks, we start out sending big ones and if we then discover that some host along the way likes its chunks smaller, - we adjust to a smaller size. This is good, so most people say - N. However, some versions of DOS NCSA telnet (and other software) - are broken and can only connect to your Linux machine if you say Y - here. See also Documentation/networking/ncsa-telnet for the location - of fixed NCSA telnet clients. + we adjust to a smaller size. This is good, so most people say N. + However, some versions of DOS NCSA telnet (and other software) are + broken and can only connect to your Linux machine if you say Y here. + See also Documentation/networking/ncsa-telnet for the location of + fixed NCSA telnet clients. Disable NAGLE algorithm (normally enabled) CONFIG_TCP_NAGLE_OFF The NAGLE algorithm works by requiring an acknowledgment before sending small IP frames (= packets). This keeps tiny telnet and rlogin packets from congesting Wide Area Networks. Most people - strongly recommend to say N here, thereby leaving NAGLE - enabled. Those programs that would benefit from disabling this - facility can do it on a per connection basis themselves. + strongly recommend to say N here, thereby leaving NAGLE enabled. + Those programs that would benefit from disabling this facility can + do it on a per connection basis themselves. IP: Drop source routed frames CONFIG_IP_NOSR Usually, the originator of an IP frame (= packet) specifies only the destination, and the hosts along the way do the routing, i.e. they - decide how to forward the frame. However, there is a feature of the + decide how to forward the frame. However, there is a feature of the IP protocol that allows to specify the full route for a given frame already at its origin. A frame with such a fully specified route is - called "source routed". The question now is whether we should honour - these route requests when such frames arrive, or if we should - drop all those frames instead. Honouring them can introduce security - problems (and is rarely a useful feature), and hence it is recommended - that you say Y here unless you really know what you're doing. + called "source routed". The question now is whether we should + honour these route requests when such frames arrive, or if we should + drop all those frames instead. Honouring them can introduce + security problems (and is rarely a useful feature), and hence it is + recommended that you say Y here unless you really know what you're + doing. IP: Allow large windows (not recommend if <16Mb of memory) CONFIG_SKB_LARGE - On high speed, long distance networks the performance limit on + On high speed, long distance networks the performance limit on networking becomes the amount of data a machine can buffer until the - other end confirms its reception. (At 45Mbit/second there are a lot - of bits between New York and London ..). This option allows larger - amounts of data to be "in flight" at a given time. It also means a user - process can require a lot more memory for network buffers and thus this - option is best only used on machines with 16Mb of memory or higher. + other end confirms its reception. (At 45Mbit/second there are a lot + of bits between New York and London...) This option allows larger + amounts of data to be "in flight" at a given time. It also means a + user process can require a lot more memory for network buffers and + thus this option is best only used on machines with 16Mb of + memory or higher. Unless you are using long links with end to end speeds of over 2Mbit - a second or satellite links this option will make no difference to + a second or satellite links this option will make no difference to performance. The IPX protocol @@ -1153,42 +1124,42 @@ Full internal IPX network CONFIG_IPX_INTERN The full internal IPX network enables you to allocate sockets on - different virtual nodes of the internal network. This is done by - evaluating the field sipx_node of the socket address given to the bind - call. So applications should always initialize the node field to 0 - when binding a socket on the primary network. In this case the socket - is assigned the default node that has been given to the kernel when - the internal network was created. - By enabling the full internal IPX network the cross-forwarding of - packets targeted at 'special' sockets to sockets listening on the - primary network is disabled. This might break existing applications, - especially RIP/SAP daemons. A RIP/SAP daemon that works well with the - full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. + different virtual nodes of the internal network. This is done by + evaluating the field sipx_node of the socket address given to the + bind call. So applications should always initialize the node field + to 0 when binding a socket on the primary network. In this case the + socket is assigned the default node that has been given to the + kernel when the internal network was created. By enabling the full + internal IPX network the cross-forwarding of packets targeted at + 'special' sockets to sockets listening on the primary network is + disabled. This might break existing applications, especially + RIP/SAP daemons. A RIP/SAP daemon that works well with the full + internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. If you don't know what you are doing, say N. Appletalk DDP CONFIG_ATALK Appletalk is the way Apple computers speak to each other on a - network. EtherTalk is the name used for appletalk over ethernet and - Localtalk is appletalk over the apple serial links. If your linux box - is connected to such a network and you want to join the conversation, - say Y. You will need to use the netatalk package so that your Linux - box can act as a print and file server for macs as well as access - appletalk printers. Check out - http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on the - WWW for details (to browse the WWW, you need to have access to a - machine on the Internet that has one of the programs lynx, netscape - or Mosaic). The NET-2-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO contains valuable information - as well. This driver is also available as a module ( = code which - can be inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read - Documentation/modules.txt. I hear that the GNU boycott of Apple is - over, so even politically correct people are allowed to say Y here. - At the time the kernel is released the localtalk drivers are not - yet ready to ship. The kernel however supports localtalk and when - such drivers become available all you will need to do is download - and install the localtalk driver. + network. EtherTalk is the name used for appletalk over ethernet + and Localtalk is appletalk over the apple serial links. If your + linux box is connected to such a network and you want to join the + conversation, say Y. You will need to use the netatalk package + so that your Linux box can act as a print and file server for + macs as well as access appletalk printers. Check out + http://artoo.hitchcock.org/~flowerpt/projects/linux-netatalk/ on + the WWW for details (to browse the WWW, you need to have access to + a machine on the Internet that has one of the programs lynx, + netscape or Mosaic). The NET-2-HOWTO, available via ftp (user: + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO contains + valuable information as well. This driver is also available as a + module (= code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. I hear that the GNU + boycott of Apple is over, so even politically correct people are + allowed to say Y here. At the time the kernel is released the + localtalk drivers are not yet ready to ship. The kernel however + supports localtalk and when such drivers become available all you + will need to do is download and install the localtalk driver. Amateur Radio AX.25 Level 2 CONFIG_AX25 @@ -1209,8 +1180,8 @@ anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also want to check out the file Documentation/networking/ax25.txt in the kernel source. More information about digital amateur radio in - general is on the WWW at - http://www.cis.ohio-state.edu/hypertext/faq/usenet/radio/ham-radio/digital-faq/faq.html + general is on the WWW at http://www.cis.ohio-state.edu/ + /hypertext/faq/usenet/radio/ham-radio/digital-faq/faq.html (To browse the WWW, you need to have access to a machine on the Internet that has one of the programs lynx, netscape or Mosaic). @@ -1220,14 +1191,13 @@ routing. A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the HAM-HOWTO, available via ftp (user: - anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You also might - also want to check out the file - Documentation/networking/ax25.txt. More information about digital - amateur radio in general is on the WWW at - http://www.cis.ohio-state.edu/hypertext/faq/usenet/radio/ham-radio/digital-faq/faq.html + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. You also might + also want to check out the file Documentation/networking/ax25.txt. + More information about digital amateur radio in general is on the + WWW at http://www.cis.ohio-state.edu + /hypertext/faq/usenet/radio/ham-radio/digital-faq/faq.html (To browse the WWW, you need to have access to a machine on the - Internet that has one of the programs lynx, netscape or - Mosaic). + Internet that has one of the programs lynx, netscape or Mosaic). AX.25 over Ethernet CONFIG_BPQETHER @@ -1241,33 +1211,32 @@ CONFIG_BRIDGE If you say Y here, then your Linux box will be able to act as an ethernet bridge, which means that the different ethernet segments it - is connected to will appear as one ethernet to the - participants. Several such bridges can work together to create even - larger networks of ethernets using the IEEE802.1 spanning tree - algorithm. As this is a standard, Linux bridges will interwork - properly with other third party bridge products. In order to use - this, you'll need the bridge configuration tools available via ftp - (user: anonymous) from shadow.cabi.net. Note that if your box acts - as a bridge, it probably contains several ethernet devices, but the - kernel is not able to recognize more than one at boot time without - help; for details read the Multiple-Ethernet-mini-HOWTO, available - via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is - still in test. If unsure, say N. + is connected to will appear as one ethernet to the participants. + Several such bridges can work together to create even larger + networks of ethernets using the IEEE802.1 spanning tree algorithm. + As this is a standard, Linux bridges will interwork properly with + other third party bridge products. In order to use this, you'll need + the bridge configuration tools available via ftp (user: anonymous) + from shadow.cabi.net. Note that if your box acts as a bridge, it + probably contains several ethernet devices, but the kernel is not + able to recognize more than one at boot time without help; for + details read the Multiple-Ethernet-mini-HOWTO, available via ftp + (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + The Bridging code is still in test. If unsure, say N. Kernel/User network link driver (EXPERIMENTAL) CONFIG_NETLINK This driver allows for two-way communication between certain parts of the kernel or modules and user processes; the user processes are able to read from and write to character special files in the /dev - directory having major mode 36. So far, the kernel uses it to + directory having major mode 36. So far, the kernel uses it to publish some network related information if you enable "Routing - messages", below. Say Y if you want to experiment with it; this is - EXPERIMENTAL code, which means that it need not be completely stable. - You need to include this if you want to use arpd, a daemon that - helps keep the internal ARP cache (a mapping between IP addresses - and hardware addresses on the local network) small. If unsure, say - N. + messages", below. Say Y if you want to experiment with it; this is + EXPERIMENTAL code, which means that it need not be completely + stable. You need to include this if you want to use arpd, a daemon + that helps keep the internal ARP cache (a mapping between IP + addresses and hardware addresses on the local network) small. + If unsure, say N. Routing messages CONFIG_RTNETLINK @@ -1281,27 +1250,26 @@ If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or any other SCSI device under Linux, say Y and make sure that you know the name of your SCSI host adapter (the card inside your computer - that "speaks" the SCSI protocol), because you will be asked for - it. You also need to say Y here if you want support for the parallel - port version of the 100MB IOMEGA ZIP drive. Please read the - SCSI-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt and - Documentation/scsi.txt. + that "speaks" the SCSI protocol), because you will be asked for it. + You also need to say Y here if you want support for the parallel + port version of the 100MB IOMEGA ZIP drive. Please read the + SCSI-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu: + /pub/Linux/docs/HOWTO. This driver is also available as a module + (= code which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read Documentation/modules.txt and Documentation/scsi.txt. SCSI disk support CONFIG_BLK_DEV_SD If you want to use a SCSI harddisk or the SCSI or parallel port - version of the IOMEGA ZIP drive under Linux, say Y and read the - SCSI-HOWTO, available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI - CDROMs. This driver is also available as a module ( = code which can - be inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read + version of the IOMEGA ZIP drive under Linux, say Y and read + the SCSI-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. This is NOT for SCSI CDROMs. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read Documentation/modules.txt and Documentation/scsi.txt. - + SCSI tape support CONFIG_CHR_DEV_ST If you want to use a SCSI tapedrive under Linux, say Y and read the @@ -1316,13 +1284,12 @@ SCSI CDROM support CONFIG_BLK_DEV_SR If you want to use a SCSI CDROM under Linux, say Y and read the - SCSI-HOWTO and the CDROM-HOWTO from - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also make sure to enable the - ISO9660 filesystem later. This driver is also available as a module - ( = code which can be inserted in and removed from the running - kernel whenever you want). If you want to compile it as a module, - say M here and read Documentation/modules.txt and - Documentation/scsi.txt . + SCSI-HOWTO and the CDROM-HOWTO from sunsite.unc.edu: + /pub/Linux/docs/HOWTO. Also make sure to enable the ISO9660 + filesystem later. This driver is also available as a module ( = code + which can be inserted in and removed from the running kernel + whenever you want). If you want to compile it as a module, say M + here and read Documentation/modules.txt and Documentation/scsi.txt. SCSI generic support CONFIG_CHR_DEV_SG @@ -1345,7 +1312,7 @@ CONFIG_SCSI_MULTI_LUN If you have a SCSI device that supports more than one LUN (Logical Unit Number), e.g. a CD jukebox, and only one LUN is detected, you - can say Y here to force the SCSI driver to probe for multiple LUNs. + can say Y here to force the SCSI driver to probe for multiple LUNs. A SCSI device with multiple LUNs acts logically like multiple SCSI devices. The vast majority of SCSI devices have only one LUN, and so most people can say N here and should in fact do so, because it @@ -1355,7 +1322,7 @@ CONFIG_SCSI_CONSTANTS The error messages regarding your SCSI hardware will be easier to understand if you enable this; it will enlarge your kernel by about - 12KB. If in doubt, say Y. + 12KB. If in doubt, say Y. AdvanSys SCSI support CONFIG_SCSI_ADVANSYS @@ -1384,112 +1351,106 @@ 3.4 of the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that Trantor was recently purchased by Adaptec, and some former Trantor products are - being sold under the Adaptec name. If it doesn't work out of - the box, you may have to change some settings in - drivers/scsi/aha1542.h. If you want to compile this 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. - + being sold under the Adaptec name. If it doesn't work out of the + box, you may have to change some settings in drivers/scsi/aha1542.h. + If you want to compile this 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. + Adaptec AHA1740 support CONFIG_SCSI_AHA1740 - This is support for a SCSI host adapter. It is explained in section - 3.5 of the SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of - the box, you may have to change some settings in - drivers/scsi/aha1740.h. This driver is also available as a module ( - = code which can be inserted in and removed from the running kernel - whenever you want). If you want to compile it as a module, say M - here and read Documentation/modules.txt. + This is support for a SCSI host adapter. It is explained in + section 3.5 of the SCSI-HOWTO, available via ftp (user: anonymous) + at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out + of the box, you may have to change some settings in + drivers/scsi/aha1740.h. This driver is also available as a module + ( = code which can be inserted in and removed from the running + kernel whenever you want). If you want to compile it as a module, + say M here and read Documentation/modules.txt. Adaptec AIC7xxx chipset SCSI controller support CONFIG_SCSI_AIC7XXX - This is support for the various aic7xxx based Adaptec SCSI controllers. - These include the 274x EISA cards, 284x VLB cards, 294x PCI cards, - 394x PCI cards, 3985 PCI card, and several versions of the Adaptec - built-in SCSI controllers on various PC motherboards. Information on - the configuration options for this controller can be found by checking - the help file for each of the available configuration options. - -Enable tagged command queueing -CONFIG_AIC7XXX_TAGGED_QUEUEING - Tagged command queueing is used to allow acceptable devices to have more - than one SCSI command active on the SCSI bus at the same time. Regardless - of this option setting, only devices that report they are capable of - tagged queueing will use this support. This option is highly recommended - if you use any SCSI hard drives on your aic7xxx SCSI controller for - performance reasons. Default: Y + This is support for the various aic7xxx based Adaptec SCSI + controllers. These include the 274x EISA cards, 284x VLB cards, + 294x PCI cards, 394x PCI cards, 3985 PCI card, and several versions + of the Adaptec built-in SCSI controllers on various PC motherboards. + Information on the configuration options for this controller can be + found by checking the README.aic7xxx file, usually in + /usr/src/linux/drivers/scsi. Override driver defaults for commands per LUN CONFIG_OVERRIDE_CMDS - Use this option to allow you to override the default maximum number of - commands that a single device on the aic7xxx controller is allowed to have - active at one time. This option only effects tagged queueing capable - devices. The driver uses a "failsafe" value of 8 by default. This is - much lower than many devices can handle, but left in place for safety sake. + Use this option to allow you to override the default maximum number + of commands that a single device on the aic7xxx controller is + allowed to have active at one time. This option only effects tagged + queueing capable devices. The driver uses a "failsafe" value of 8 + by default. This is much lower than many devices can handle, but + left in place for safety sake. + NOTE: This does not actually enabled tagged queueing on any + particular device. The driver has changed in this respect. Please + see the file README.aic7xxx in /usr/src/linux/drivers/scsi for more + information on how to get particular devices to use tagged command + queueing. Default: N Maximum number of commands per LUN CONFIG_AIC7XXX_CMDS_PER_LUN - Specify the maximum number of commands per lun you would like to allocate - per device. Reasonable figures are in the range of 14 to 32 commands per - device, but depending on hardware could be increased or decreased from - that figure. If the number is too high for any particular device, the - driver will automatically compensate usually after only 10 minutes of - uptime and will issue a message to alert you to the fact that the number - of commands for that device has been reduced. It will not hinder - performance if a portion of your devices eventually have their commands - per lun reduced, but is a waste of memory if all of your devices end - up reducing this number down to a more reasonable figure. Default: 24 - -Enable SCB paging -CONFIG_AIC7XXX_PAGE_ENABLE - This option allows the driver to issue more commands to the controller - than it has physical space to store. Since some aic7xxx chipsets can only - store 3 commands, and the majority can only store 16, not enabling this - capability can effectively negate any performance increase you might get - from enabling Tagged Queueing. Default: Y + Specify the maximum number of commands per lun you would like to + allocate per device. Reasonable figures are in the range of 14 to + 32 commands per device, but depending on hardware could be increased + or decreased from that figure. If the number is too high for any + particular device, the driver will automatically compensate usually + after only 10 minutes of uptime and will issue a message to alert + you to the fact that the number of commands for that device has been + reduced. It will not hinder performance if a portion of your + devices eventually have their commands per lun reduced, but is a + waste of memory if all of your devices end up reducing this number + down to a more reasonable figure. Default: 24 Collect statistics to report in /proc CONFIG_AIC7XXX_PROC_STATS - This option tells the driver to keep track of how many commands have been - sent to each particular device and report that information to the user - via the /proc/scsi/aic7xxx/x file, where x is the number of the aic7xxx - controller you want the information on. This adds a small amount of - overhead to each and every SCSI command the aic7xxx driver handles, so if - you aren't really interested in this information, it is best to leave it - disabled. Default: N + This option tells the driver to keep track of how many commands have + been sent to each particular device and report that information to + the user via the /proc/scsi/aic7xxx/x file, where x is the number + of the aic7xxx controller you want the information on. This adds + a small amount of overhead to each and every SCSI command the + aic7xxx driver handles, so if you aren't really interested in this + information, it is best to leave it disabled. Default: N Delay in seconds after SCSI bus reset CONFIG_AIC7XXX_RESET_DELAY - This sets how long the driver will wait after resetting the SCSI bus before - attempting to communicate with the devices on the SCSI bus again. This - delay will be used during the reset phase at bootup time as well as after - any reset that might occur during normal operation. Reasonable numbers - range anywhere from 5 to 15 seconds depending on your devices. DAT tape - drives are notorious for needing more time after a bus reset to be - ready for the next command, but most hard drives and CD-ROM devices are - ready in only a few seconds. This option has a maximum upper limit of - 20 seconds to avoid bad interactions between the aic7xxx driver and the - rest of the linux kernel. The default value is a "failsafe" value that - should work with just about any device. Default: 15 + This sets how long the driver will wait after resetting the SCSI bus + before attempting to communicate with the devices on the SCSI bus + again. This delay will be used during the reset phase at bootup + time as well as after any reset that might occur during normal + operation. Reasonable numbers range anywhere from 5 to 15 seconds + depending on your devices. DAT tape drives are notorious for needing + more time after a bus reset to be ready for the next command, but + most hard drives and CD-ROM devices are ready in only a few seconds. + This option has a maximum upper limit of 20 seconds to avoid bad + interactions between the aic7xxx driver and the rest of the linux + kernel. The default value has been reduced. If this doesn't work + with your hardware, try increasing this value. Default: 5 BusLogic SCSI support CONFIG_SCSI_BUSLOGIC - This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters. - Consult the SCSI-HOWTO, available via anonymous ftp from sunsite.unc.edu in - /pub/Linux/docs/HOWTO, and the files README.BusLogic and README.FlashPoint in - drivers/scsi for more information. If this driver does not work correctly - without modification, please contact the author, Leonard N. Zubkoff, by email - to lnz@dandelion.com. You can also build this driver as a module ( = code - which can be inserted in and removed from the running kernel whenever you - want), but only a single instance may be loaded. If you want to compile it + This is support for BusLogic MultiMaster and FlashPoint SCSI Host + Adapters. Consult the SCSI-HOWTO, available via anonymous ftp from + sunsite.unc.edu:/pub/Linux/docs/HOWTO, and the files README.BusLogic + and README.FlashPoint in drivers/scsi for more information. If this + driver does not work correctly without modification, please contact + the author, Leonard N. Zubkoff, by email to lnz@dandelion.com. + You can also build this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + but only a single instance may be loaded. If you want to compile it as a module, say M here and read Documentation/modules.txt. Omit BusLogic SCSI FlashPoint support CONFIG_SCSI_OMIT_FLASHPOINT - This option allows you to omit the FlashPoint support from the BusLogic - SCSI driver. The FlashPoint SCCB Manager code is substantial, so users of - MultiMaster Host Adapters may wish to omit it. + This option allows you to omit the FlashPoint support from the + BusLogic SCSI driver. The FlashPoint SCCB Manager code is + substantial, so users of MultiMaster Host Adapters may wish to + omit it. DTC3180/3280 SCSI support CONFIG_SCSI_DTC3280 @@ -1528,29 +1489,30 @@ UltraStor 14F/34F support CONFIG_SCSI_U14_34F This is support for the UltraStor 14F and 34F SCSI-2 host adapters. - The source at drivers/scsi/u14-34f.c contains some information about - this hardware. If the driver doesn't work out of the box, you may have - to change some settings in drivers/scsi/u14-34f.c. - Read the SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that there is also another - driver for the same hardware: "UltraStor SCSI support", below. - You should enable both only if you want 24F support as well. This - driver is also available as a module ( = code which can be inserted - in and removed from the running kernel whenever you want). If you - want to compile it as a module, say M here and read + The source at drivers/scsi/u14-34f.c contains some information about + this hardware. If the driver doesn't work out of the box, you may + have to change some settings in drivers/scsi/u14-34f.c. + Read the SCSI-HOWTO, available via ftp (user: anonymous) at + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that there is also + another driver for the same hardware: "UltraStor SCSI support", + below. You should enable both only if you want 24F support as well. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read Documentation/modules.txt. Future Domain 16xx SCSI support CONFIG_SCSI_FUTURE_DOMAIN This is support for Future Domain's 16-bit SCSI host adapters - (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and other - adapters based on the Future Domain chipsets (Quantum ISA-200S, - ISA-250MG; Adaptec AHA-2920; and at least one IBM board). It is - explained in section 3.7 of the SCSI-HOWTO, available via ftp (user: - anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is - also available as a module ( = code which can be inserted in and - removed from the running kernel whenever you want). If you want to - compile it as a module, say M here and read Documentation/modules.txt. + (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) + and other adapters based on the Future Domain chipsets (Quantum + ISA-200S, ISA-250MG; Adaptec AHA-2920; and at least one IBM board). + It is explained in section 3.7 of the SCSI-HOWTO, available via ftp + (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + Documentation/modules.txt. Generic NCR5380/53c400 SCSI support CONFIG_SCSI_GENERIC_NCR5380 @@ -1563,7 +1525,7 @@ ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. - + Enable NCR53c400 extensions CONFIG_SCSI_GENERIC_NCR53C400 This enables certain optimizations for the NCR53c400 scsi cards. You @@ -1592,19 +1554,19 @@ whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. -always negotiate synchronous transfers +Always negotiate synchronous transfers CONFIG_SCSI_NCR53C7xx_sync In general, this is good; however, it is a bit dangerous since there are some broken SCSI devices out there. Take your chances. Safe bet is N. -allow FAST-SCSI [10MHz] +Allow FAST-SCSI [10MHz] CONFIG_SCSI_NCR53C7xx_FAST - This will enable 10MHz FAST-SCSI transfers with your host - adapter. Some systems have problems with that speed, so it's safest - to say N here. - -allow DISCONNECT + This will enable 10MHz FAST-SCSI transfers with your host adapter. + Some systems have problems with that speed, so it's safest to say N + here. + +Allow DISCONNECT CONFIG_SCSI_NCR53C7xx_DISCONNECT This enables the disconnect/reconnect feature of the NCR SCSI controller. When this is enabled, a slow SCSI device will not lock @@ -1622,14 +1584,14 @@ of PCI-SCSI controllers. This driver supports parity checking, tagged command queuing, fast scsi II transfer up to 10 MB/s with narrow scsi devices and 20 MB/s with wide scsi devices. - Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 + Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 controllers has been recently added to the driver. Please read drivers/scsi/README.ncr53c8xx for more information. Linux/i386 and Linux/Alpha are supported by this driver. -synchronous data transfers frequency +Synchronous data transfers frequency CONFIG_SCSI_NCR53C8XX_SYNC - SCSI-2 specifications allow scsi devices to negotiate a synchronous + SCSI-2 specifications allow scsi devices to negotiate a synchronous transfer period of 25 nano-seconds or more. The transfer period value is 4 times the agreed transfer period. So, data can be transferred at a 10 MHz frequency, allowing 10 @@ -1649,16 +1611,16 @@ Use a 25 ns period for 10 Mhz synchronous data transfers. If you don't know what to do now, go with the default. -use normal IO +Use normal IO CONFIG_SCSI_NCR53C8XX_IOMAPPED This option allows you to force the driver to use normal IO. - Memory mapped IO has less latency than normal IO and works for most + Memory mapped IO has less latency than normal IO and works for most Intel-based hardware. - Under Linux/Alpha only normal IO is currently supported by the driver - and so, this option has no effect. + Under Linux/Alpha only normal IO is currently supported by the + driver and so, this option has no effect. The normal answer therefore is N. -not allow targets to disconnect +Not allow targets to disconnect CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT This option is only provided for safety if you suspect some scsi device of yours to not support properly the target-disconnect @@ -1666,7 +1628,7 @@ not allow targets to disconnect is not reasonable if there is more than 1 device on a scsi bus. The normal answer therefore is N. -enable tagged command queuing +Enable tagged command queuing CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE This option allows you to enable tagged command queuing support at linux start-up. Some scsi devices do not properly support this @@ -1681,33 +1643,35 @@ The safe answer therefore is N. The normal answer therefore is Y. -maximum number of queued commands +Maximum number of queued commands CONFIG_SCSI_NCR53C8XX_MAX_TAGS This option allows you to specify the maximum number of commands that can be queued to a device, when tagged command queuing is possible. The default value is 4. Minimum is 2, maximum is 12. The normal answer therefore is the default one. -detect and read serial NVRAM +Detect and read serial NVRAM CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT Enable support for reading the serial NVRAM data on Symbios and - some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful for - systems with more than one Symbios compatible controller where at least - one has a serial NVRAM, or for a system with a mixture of Symbios and - Tekram cards. Enables setting the boot order of host adaptors - to something other than the default order or "reverse probe" order. - Also enables Symbios and Tekram cards to be distinguished so - CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in a system with a - mixture of Symbios and Tekram cards so the Symbios cards can make use of - the full range of Symbios features, differential, led pin, without - causing problems for the Tekram card(s). + some Symbios compatible cards, and Tekram DC390W/U/F cards. Useful + for systems with more than one Symbios compatible controller where + at least one has a serial NVRAM, or for a system with a mixture of + Symbios and Tekram cards. Enables setting the boot order of host + adaptors to something other than the default order or "reverse + probe" order. Also enables Symbios and Tekram cards to be + distinguished so CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT may be set in + a system with a mixture of Symbios and Tekram cards so the Symbios + cards can make use of the full range of Symbios features, + differential, led pin, without causing problems for the Tekram + card(s). (added by Richard Waltham: dormouse@farsrobt.demon.co.uk) - Also enables setting host and targets SCSI features as defined in the - user setup for each host using a serial NVRAM (added by the maintainer). + Also enables setting host and targets SCSI features as defined in + the user setup for each host using a serial NVRAM (added by the + maintainer). The default answer is N, the normal answer should be Y. Read drivers/scsi/README.ncr53c8xx for more information. -assume boards are SYMBIOS compatible +Assume boards are SYMBIOS compatible CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT This option allows you to enable some features depending on GPIO wiring. These General Purpose Input/Output pins can be used for @@ -1722,97 +1686,95 @@ 0x12). This option must be set to N if your system has at least one 53C8XX based scsi board with a vendor-specific BIOS (example: Tekram DC-390/U/W/F). If unsure, say N. - However, if all your non Symbios compatible boards have NvRAM, setting - option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver to - distinguish Symbios compatible boards from other ones. - So, you can answer Y if all non Symbios compatible boards have NVRAM. + However, if all your non Symbios compatible boards have NVRAM, + setting option CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT allows the driver + to distinguish Symbios compatible boards from other ones. So, + you can answer Y if all non Symbios compatible boards have NVRAM. Always IN2000 SCSI support CONFIG_SCSI_IN2000 - This is support for an ISA bus SCSI host adapter. You'll find - more information in drivers/scsi/in2000.readme. If it doesn't - work out of the box, you may have to change the jumpers for IRQ - or address selection. If you want to compile this 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. + This is support for an ISA bus SCSI host adapter. You'll find more + information in drivers/scsi/in2000.readme. If it doesn't work out + of the box, you may have to change the jumpers for IRQ or address + selection. If you want to compile this 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. PAS16 SCSI support CONFIG_SCSI_PAS16 - This is support for a SCSI host adapter. It is explained in section + This is support for a SCSI host adapter. It is explained in section 3.10 of the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may have to change some settings in drivers/scsi/pas16.h. - + Qlogic FAS SCSI support CONFIG_SCSI_QLOGIC_FAS This driver works only with the ISA, VLB, and PCMCIA versions of the Qlogic FastSCSI! cards as well as any other card based on the FASXX chip (including the Control Concepts SCSI/IDE/SIO/PIO/FDC cards); it - does NOT support the PCI version. The PCI versions are supported by + does NOT support the PCI version. The PCI versions are supported by the Qlogic ISP driver though. Information about this driver is contained in drivers/scsi/README.qlogicfas. You should also read the SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. Qlogic ISP SCSI support (EXPERIMENTAL) CONFIG_SCSI_QLOGIC_ISP This driver works for all QLogic PCI SCSI host adapters (IQ-PCI, - IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter - card is supported by the "AM53/79C974 PCI SCSI" driver). If you say - Y here, make sure to say Y to "PCI BIOS support" as well. More - information is contained in the file - drivers/scsi/README.qlogicisp. You should also read the SCSI-HOWTO, - available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. + IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter + card is supported by the "AM53/79C974 PCI SCSI" driver.) If you + say Y here, make sure to say Y to "PCI BIOS support" as well. More + information is contained in the file drivers/scsi/README.qlogicisp. + You should also read the SCSI-HOWTO, available via ftp (user: + anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you + want). If you want to compile it as a module, say M here and read + Documentation/modules.txt. Seagate ST-02 and Future Domain TMC-8xx SCSI support CONFIG_SCSI_SEAGATE - These are 8-bit SCSI controllers; the ST-01 is also supported by this - driver. It is explained in section 3.9 of the SCSI-HOWTO, available - via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the - box, you may have to change some settings in drivers/scsi/seagate.h. + These are 8-bit SCSI controllers; the ST-01 is also supported by + this driver. It is explained in section 3.9 of the SCSI-HOWTO, + available via ftp (user: anonymous) at sunsite.unc.edu: + /pub/Linux/docs/HOWTO. If it doesn't work out of the box, you may + have to change some settings in drivers/scsi/seagate.h. This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you - want). If you want to compile it as a module, say M here and read + want). If you want to compile it as a module, say M here and read Documentation/modules.txt. Trantor T128/T128F/T228 SCSI support CONFIG_SCSI_T128 - This is support for a SCSI host adapter. It is explained in section + This is support for a SCSI host adapter. It is explained in section 3.11 of the SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of - the box, you may have to change some settings in - drivers/scsi/t128.h. Note that Trantor was recently purchased by - Adaptec, and some former Trantor products are being sold under the - Adaptec name. + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the + box, you may have to change some settings in drivers/scsi/t128.h. + Note that Trantor was recently purchased by Adaptec, and some former + Trantor products are being sold under the Adaptec name. UltraStor SCSI support CONFIG_SCSI_ULTRASTOR This is support for the UltraStor 14F, 24F and 34F SCSI-2 host - adapter family. This driver is explained in section 3.12 of the + adapter family. This driver is explained in section 3.12 of the SCSI-HOWTO, available via ftp (user: anonymous) at - sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the - box, you may have to change some settings in - drivers/scsi/ultrastor.h. If you want to compile this as a module ( - = code which can be inserted in and removed from the running kernel + sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of + the box, you may have to change some settings in + drivers/scsi/ultrastor.h. If you want to compile this 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. Note that there is also another driver for UltraStor hardware: "UltraStor 14F/34F support", above. - + 7000FASST SCSI support CONFIG_SCSI_7000FASST This driver supports the Western Digital 7000 SCSI host adapter. Some information is in the source: drivers/scsi/wd7000.c. This driver is also available as a module ( = code which can be inserted - in and removed from the running kernel whenever you want). If you + in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. @@ -1821,10 +1783,10 @@ This driver supports all the EATA/DMA-compliant SCSI host adapters and does not need any BIOS32 service. DPT ISA and all EISA i/o addresses are probed looking for the "EATA" - signature. If "PCI bios support" is enabled, the addresses of all the - PCI SCSI controllers reported by BIOS32 are probed as well. + signature. If "PCI bios support" is enabled, the addresses of all + the PCI SCSI controllers reported by BIOS32 are probed as well. Note that there is also another driver for the same hardware: - "EATA-DMA support". You should enable only one of them. + "EATA-DMA support". You should enable only one of them. You want to read the start of drivers/scsi/eata.c and the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you want to compile this @@ -1834,9 +1796,9 @@ NCR53c406a SCSI support CONFIG_SCSI_NCR53C406A - This is support for the NCR53c406a SCSI host adapter. For user + This is support for the NCR53c406a SCSI host adapter. For user configurable parameters, check out drivers/scsi/NCR53c406.c in the - kernel source. Also read the SCSI-HOWTO, available via ftp (user: + kernel source. Also read the SCSI-HOWTO, available via ftp (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. 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 @@ -1846,7 +1808,7 @@ 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 + This driver does _not_ support the DC390W/U/F adaptor with the NCR/Symbios chips. AM53/79C974 PCI SCSI support @@ -1855,15 +1817,17 @@ 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. GDT SCSI Disk Array Controller support CONFIG_SCSI_GDTH - This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) + This is a driver for all SCSI Disk Array Controllers (EISA/ISA/PCI) manufactured by ICP vortex. It is documented in the kernel source in drivers/scsi/gdth.c and drivers/scsi/gdth.h. This driver is also available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt. + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. IOMEGA Parallel Port ZIP drive SCSI support CONFIG_SCSI_PPA @@ -1882,6 +1846,15 @@ the SCSI version of the ZIP drive: it will be supported automatically if you enabled the generic "SCSI disk support", above. +IOMEGA ZIP drive - Buggy EPP chipset support +CONFIG_SCSI_PPA_HAVE_PEDANTIC + Contacts with the Iomega driver development team indicate there are + a few reputably bad EPP implementations in existance. The following + mainboard chipsets will probably require the PEDANTIC option to + reliably transfer data: + Winbond xxx837 + National Semiconductor PC87306 (early revisions) + Network device support? CONFIG_NETDEVICES You can say N here in case you don't intend to connect to any other @@ -1908,6 +1881,7 @@ read Olaf Kirch's excellent book "Network Administrator's Guide", to be found in sunsite.unc.edu:/pub/Linux/docs/LDP. If unsure, say Y. +Ethernet (10 or 100Mbit) CONFIG_NET_ETHERNET Ethernet is the most common protocol used on Local Area Networks (LANs) in universities or companies. 10-base-2 and 10-base-T and @@ -1924,88 +1898,87 @@ CONFIG_DUMMY This is essentially a bit-bucket device (i.e. traffic you send to this device is consigned into oblivion) with a configurable IP - address. It is most commonly used in order to make your currently - inactive SLIP address seem like a real address for local - programs. If you use SLIP or PPP, you might want to enable it. Read - about it in the Network Administrator's Guide, available via ftp - (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Since - this thing comes often handy, the default is Y. It won't enlarge - your kernel either. What a deal. If you want to compile this as a + address. It is most commonly used in order to make your currently + inactive SLIP address seem like a real address for local programs. + If you use SLIP or PPP, you might want to enable it. Read about it + in the Network Administrator's Guide, available via ftp (user: + anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Since this + thing comes often handy, the default is Y. It won't enlarge your + kernel either. What a deal. If you want to compile this 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. If you want to use more than one dummy device at a time, you need to compile it as a module. Instead of 'dummy', it will they will then be called 'dummy0', 'dummy1' etc. - + SLIP (serial line) support CONFIG_SLIP Say Y if you intend to use SLIP or CSLIP (compressed SLIP) to connect to your Internet service provider or to connect to some - other local Unix box or if you want to configure your Linux box as a - Slip/CSlip server for other people to dial in. SLIP (Serial Line + other local Unix box or if you want to configure your Linux box as + a Slip/CSlip server for other people to dial in. SLIP (Serial Line Internet Protocol) is the protocol used to send Internet traffic - over telephone lines or serial cables (also known as - nullmodems). Normally, your access provider has to support SLIP in - order for you to be able to use it, but there is now a SLIP emulator - called SLiRP around (available via ftp (user: anonymous) from - sunsite.unc.edu:/pub/Linux/system/Network/serial/) which allows you - to use SLIP over a regular dial up shell connection. If you plan to - use SLiRP, make sure to say Y to CSLIP, below. The NET-2-HOWTO, - available via ftp (user: anonymous) in - sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure - SLIP. Note that you don't need this option if you just want to run - term (term is a program which gives you almost full Internet - connectivity if you have a regular dial up shell account on some - Internet connected Unix computer. Read the Term-HOWTO). SLIP support - will enlarge your kernel by about 4kB. If unsure, say N. If you - want to compile this 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 as well as + over telephone lines or serial cables (also known as nullmodems). + Normally, your access provider has to support SLIP in order for you + to be able to use it, but there is now a SLIP emulator called SLiRP + around (available via ftp (user: anonymous) from sunsite.unc.edu: + /pub/Linux/system/Network/serial/) which allows you to use SLIP over + a regular dial up shell connection. If you plan to use SLiRP, make + sure to say Y to CSLIP, below. The NET-2-HOWTO, available via ftp + (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains + how to configure SLIP. Note that you don't need this option if you + just want to run term (term is a program which gives you almost full + Internet connectivity if you have a regular dial up shell account on + some Internet connected Unix computer. Read the Term-HOWTO). SLIP + support will enlarge your kernel by about 4kB. If unsure, say N. + If you want to compile this 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 as well as Documentation/networking/net-modules.txt. - + CSLIP compressed headers CONFIG_SLIP_COMPRESSED This protocol is faster than SLIP because it uses compression on the TCP/IP headers (not on the data itself), but it has to be supported on both ends. Ask your access provider if you are not sure and say - Y, just in case. You will still be able to use plain SLIP. If you + Y, just in case. You will still be able to use plain SLIP. If you plan to use SLiRP, the SLIP emulator (available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/system/Network/serial/) which allows you to use SLIP over a regular dial up shell - connection, you definitely want to say Y here. The NET-2-HOWTO, + connection, you definitely want to say Y here. The NET-2-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO, explains how to configure - CSLIP. This won't enlarge your kernel. + CSLIP. This won't enlarge your kernel. Keepalive and linefill CONFIG_SLIP_SMART Adds additional capabilities to the SLIP driver to support the - RELCOM line fill and keepalive monitoring. Ideal on poor quality + RELCOM line fill and keepalive monitoring. Ideal on poor quality analogue lines. Six bit SLIP encapsulation CONFIG_SLIP_MODE_SLIP6 Just occasionally you may need to run IP over hostile serial networks that don't pass all control characters or are only seven - bit. Saying Y here adds an extra mode you can use with SLIP: - "slip6". In this mode, SLIP will only send normal ascii symbols over - the serial device. Naturally, this has to be supported at the other - end of the link as well. It's good enough, for example, to run IP - over the async ports of a Camtec JNT Pad. If unsure, say N. + bit. Saying Y here adds an extra mode you can use with SLIP: + "slip6". In this mode, SLIP will only send normal ascii symbols + over the serial device. Naturally, this has to be supported at the + other end of the link as well. It's good enough, for example, to + run IP over the async ports of a Camtec JNT Pad. If unsure, say N. Radio network interfaces CONFIG_NET_RADIO - Radio based interfaces for Linux. This includes amateur radio - (AX.25), support for wireless ethernet and other systems. Note that + Radio based interfaces for Linux. This includes amateur radio + (AX.25), support for wireless ethernet and other systems. Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all the questions about radio interfaces. Some user-level drivers for scarab devices which don't require special kernel support are available via - ftp (user: anonymous) from shadow.cabi.net. If unsure, say N. + ftp (user: anonymous) from shadow.cabi.net. If unsure, say N. PPP (point-to-point) support CONFIG_PPP - PPP (Point to Point Protocol) is a newer and better SLIP. It serves + PPP (Point to Point Protocol) is a newer and better SLIP. It serves the same purpose: sending Internet traffic over telephone (and other serial) lines. Ask your access provider if they support it, because otherwise you can't use it (not quite true any more: the free @@ -2018,25 +1991,25 @@ this option if you just want to run term (term is a program which gives you almost full Internet connectivity if you have a regular dial up shell account on some Internet connected UNIX computer. Read - the Term-HOWTO). The PPP option enlarges your kernel by about - 16kB. This driver is also available as a module ( = code which can - be inserted in and removed from the running kernel whenever you - want). If you said Y to "Version information on all symbols" above, - then you cannot compile the PPP driver into the kernel; you can only - compile it as a module. If you want to compile it as a module, say M - here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. Note that, no matter what + the Term-HOWTO). The PPP option enlarges your kernel by about 16kB. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you said Y to "Version information on all symbols" above, then + you cannot compile the PPP driver into the kernel; you can only + compile it as a module. If you want to compile it as a module, + say M here and read Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. Note that, no matter what you do, the BSD compression code (used to compress the IP packets sent over the serial line; has to be supported at the other end as well) can only be compiled as a module; it is called bsd_comp.o and will show up in the directory modules once you have said "make - modules". If unsure, say N. + modules". If unsure, say N. -16 channels instead of 4 +16 channels instead of 4 CONFIG_PPP_LOTS Saying Y here will allow you to have up to 16 PPP connections - running in parallel. This is mainly useful if you intend your linux - box to act as a dial-in PPP server. Most people can say N. + running in parallel. This is mainly useful if you intend your + linux box to act as a dial-in PPP server. Most people can say N. STRIP (Starmode Radio IP) support CONFIG_STRIP @@ -2050,15 +2023,15 @@ many people into thinking that you can plug a Metricom modem into a phone line and use it as a modem.) You can use STRIP on any Linux machine with a serial port, although it is obviously most useful for - people with laptop computers. If you think you might get a Metricom + people with laptop computers. If you think you might get a Metricom radio in the future, there is no harm in saying yes to STRIP now, except that it makes the kernel a bit bigger. WIC (Radio IP bridge) CONFIG_WIC Support for the WIC parallel port radio bridge. You'll probably want - to say N. If you want to compile this driver as a module though ( = - code which can be inserted in and removed from the running kernel + to say N. If you want to compile this driver as a module though + (= code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. Z8530 SCC kiss emulation driver for AX.25 @@ -2161,7 +2134,7 @@ it as a module, say M here and read Documentation/modules.txt. Max open DLCI -CONFIG_DLCI_COUNT +CONFIG_DLCI_COUNT This is the maximal number of logical point-to-point frame relay connections (the identifiers of which are called DCLIs) that the driver can handle. The default is probably fine. @@ -2176,11 +2149,11 @@ Sangoma S502A FRAD support CONFIG_SDLA Say Y here if you need a driver for the Sangoma S502A, S502E, and - S508 Frame Relay Access Devices. These are multi-protocol - cards, but only frame relay is supported by the driver at this - time. Please read Documentation/framerelay.txt. This driver is also + S508 Frame Relay Access Devices. These are multi-protocol cards, + but only frame relay is supported by the driver at this time. + Please read Documentation/framerelay.txt. This driver is also available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile + from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. Sun LANCE Ethernet support @@ -2191,8 +2164,8 @@ Sun Intel Ethernet support CONFIG_SUN_INTEL - This is support for the intel ethernet cards on some Sun workstations - (all those with a network interface 'ie0' under SunOS). + This is support for the intel ethernet cards on some Sun + workstations (all those with a network interface 'ie0' under SunOS). Western Digital/SMC cards CONFIG_NET_VENDOR_SMC @@ -2235,8 +2208,8 @@ Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. Important: There have been many reports that, with some motherboards - mixing an SMC Ultra and an Adaptec AHA1542 SCSI card causes corruption - problems with many operating systems. + mixing an SMC Ultra and an Adaptec AHA1542 SCSI card causes + corruption problems with many operating systems. SMC Ultra32 support CONFIG_ULTRA32 @@ -2279,28 +2252,28 @@ 3COM cards CONFIG_NET_VENDOR_3COM If you have a network (ethernet) card belonging to this class, say Y - and read the Ethernet-HOWTO, available via ftp (user: anonymous) - in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to - this question doesn't directly affect the kernel: saying N will just + and read the Ethernet-HOWTO, available via ftp (user: anonymous) in + sunsite.unc.edu:/pub/Linux/docs/HOWTO. Note that the answer to this + question doesn't directly affect the kernel: saying N will just cause this configure script to skip all the questions about 3COM cards. If you say Y, you will be asked for your specific card in the following questions. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c501 support CONFIG_EL1 If you have a network (ethernet) card of this type, say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Also, consider buying a new - card, since the 3c501 is slow and obsolete. This driver is also + card, since the 3c501 is slow and obsolete. This driver is also available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile - it as a module, say M here and read Documentation/modules.txt as well - as Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini - and don't use 3c501s. + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt as + well as Documentation/networking/net-modules.txt. + If you plan to use more than one network card under linux, + read the Multiple-Ethernet-mini-HOWTO, available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini---and don't use 3c501s. 3c503 support CONFIG_EL2 @@ -2310,9 +2283,9 @@ as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c505 support @@ -2324,9 +2297,9 @@ compile this 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 as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under linux, + read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. 3c507 support @@ -2390,37 +2363,38 @@ ARCnet support CONFIG_ARCNET If you have a network card of this type, say Y and check out the - (arguably) beautiful poetry in Documentation/networking/arcnet.txt. + (arguably) beautiful poetry in Documentation/networking/arcnet.txt. You might also want to have a look at the Ethernet-HOWTO, available via ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO - (even though ARCnet is not really ethernet). This driver is also + (even though ARCnet is not really ethernet). This driver is also available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile it - as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt as + well as Documentation/networking/net-modules.txt. If you plan to + use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - + Enable arc0e (ARCnet "ether-encap" packet format) CONFIG_ARCNET_ETH - This allows you to use "ethernet encapsulation" with your ARCnet card - via the virtual arc0e device. You only need arc0e if you want to - talk to nonstandard ARCnet software, specifically, DOS/Windows-style - "NDIS" drivers. You do not need to enable this option to communicate - with industry-standard RFC1201 implementations, like the arcether.com - packet driver or most DOS/Windows ODI drivers. RFC1201 is included - automatically as the arc0 device. Please read the ARCnet - documentation in Documentation/networking/arcnet.txt for more - information about using arc0e and arc0s. + This allows you to use "ethernet encapsulation" with your ARCnet + card via the virtual arc0e device. You only need arc0e if you want + to talk to nonstandard ARCnet software, specifically, + DOS/Windows-style "NDIS" drivers. You do not need to enable this + option to communicate with industry-standard RFC1201 + implementations, like the arcether.com packet driver or most + DOS/Windows ODI drivers. RFC1201 is included automatically as the + arc0 device. Please read the ARCnet documentation in + Documentation/networking/arcnet.txt for more information about + using arc0e and arc0s. Enable arc0s (ARCnet RFC1051 packet format) CONFIG_ARCNET_1051 This allows you to use RFC1051 with your ARCnet card via the virtual arc0s device. You only need arc0s if you want to talk to ARCnet software complying with the "old" standard, specifically, the DOS - arcnet.com packet driver, Amigas running AmiTCP, and some variants of - NetBSD. You do not need to enable this option to communicate with + arcnet.com packet driver, Amigas running AmiTCP, and some variants + of NetBSD. You do not need to enable this option to communicate with industry-standard RFC1201 implementations, like the arcether.com packet driver or most DOS/Windows ODI drivers. RFC1201 is included automatically as the arc0 device. Please read the ARCnet @@ -2492,17 +2466,18 @@ FMV-181/182/183/184 support CONFIG_FMV18X If you have a Fujitsu FMV-181/182/183/184 network (ethernet) card, - say Y and read the Ethernet-HOWTO, available via ftp (user: anonymous) - in sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also - available as a module ( = code which can be inserted in and removed - from the running kernel whenever you want). If you want to compile it - as a module, say M here and read Documentation/modules.txt as well as - Documentation/networking/net-modules.txt. If you plan to use more - than one network card under linux, read the - Multiple-Ethernet-mini-HOWTO, available from - sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you use FMV-183 or - FMV-184 and it is not working, you may need to disable Plug & Play - mode of the card. + say Y and read the Ethernet-HOWTO, available via ftp (user: + anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + If you want to compile it as a module, say M here and read + Documentation/modules.txt as well as + Documentation/networking/net-modules.txt. + If you plan to use more than one network card under linux, + read the Multiple-Ethernet-mini-HOWTO, available from + sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. + If you use FMV-183 or FMV-184 and it is not working, you may need + to disable Plug & Play mode of the card. Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B @@ -2558,7 +2533,7 @@ Ottawa PI and PI/2 support CONFIG_PI This is a driver for the Ottawa Amateur Radio Club PI and PI2 cards, - which are commonly used to send internet traffic over amateur radio. + which are commonly used to send internet traffic over amateur radio. More information about these cards is on the WWW at http://hydra.carleton.ca/info/pi2.html (To browse the WWW, you need to have access to a machine on the Internet that has one of the @@ -2586,7 +2561,7 @@ This driver is fairly stable and may be compiled as a module (wavelan.o). It implements many nice feature and the Wireless Extensions (you must get the Wireless tools from the net). - For documentation, refer to : + For documentation, refer to: o the wavelan man page, wireless tools man pages o wavelan.p.h and the source code o Ethernet-HOWTO, Multiple-Ethernet-mini-HOWTO, Module-HOWTO @@ -2753,6 +2728,14 @@ Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. +TI ThunderLAN support (EXPERIMENTAL) +CONFIG_TLAN + If you have a TLAN based network card which is supported by this + driver, say Y and read the Ethernet-HOWTO. Devices currently + supported are the Compaq Netelligent 10, Netelligent 10/100, and + Internal NetFlex 3. This driver is also available as a module. + Please email feedback to james.banks@caldera.com. + Zenith Z-Note support CONFIG_ZNET The Zenith Z-Note notebook computer has a built-in network @@ -2820,13 +2803,13 @@ both drivers as modules. If you plan to use more than one network card under linux, read the Multiple-Ethernet-mini-HOWTO, available from sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. - + Token Ring driver support CONFIG_TR Token Ring is IBM's way of communication on a local network; the rest of the world uses ethernet. If you are connected to a token - ring network and want to use your Token Ring card under Linux, say Y. - Most people can say N here. + ring network and want to use your Token Ring card under Linux, + say Y. Most people can say N here. IBM Tropic chipset based adapter support CONFIG_IBMTR @@ -2853,10 +2836,10 @@ Most of these drivers use a file include/linux/.h where you can define your interface parameters and switch some internal goodies. - All these CDROM drivers are also usable as a module (= code which can - be inserted in and removed from the running kernel whenever you want). - If you want to compile them as module, say M instead of Y and read - Documentation/modules.txt. + All these CDROM drivers are also usable as a module ( = code which + can be inserted in and removed from the running kernel whenever you + want). If you want to compile them as module, say M instead of Y + and read Documentation/modules.txt. If you want to use any of these CDROM drivers, you also have to say Y to "ISO9660 cdrom filesystem support" below (this answer will get "defaulted" for you if you enable any of the Linux CDROM drivers). @@ -2875,14 +2858,14 @@ Standard Mitsumi [no XA/Multisession] CDROM support CONFIG_MCD This is the older of the two drivers for the older Mitsumi models - LU-005, FX-001 and FX-001D. This is not the right driver for the - FX-001DE and the triple or quad speed models (all these are IDE/ATAPI - models). + LU-005, FX-001 and FX-001D. This is not the right driver for + the FX-001DE and the triple or quad speed models (all these are + IDE/ATAPI models). With the old LU-005 model, the whole drive chassis slides out for - cd insertion. The FX-xxx models use a motorized tray type mechanism. - Note that this driver does not support XA or MultiSession CDs (PhotoCDs). - There is a new driver (next question) which can do this. If you want - that one, say N here. + cd insertion. The FX-xxx models use a motorized tray type mechanism. + Note that this driver does not support XA or MultiSession CDs + (PhotoCDs). There is a new driver (next question) which can do + this. If you want that one, say N here. If the driver doesn't work out of the box, you might want to have a look at linux/include/linux/mcd.h. @@ -2890,10 +2873,10 @@ CONFIG_MCDX Use this driver if you want to be able to read XA or MultiSession CDs (PhotoCDs) as well as ordinary CDs with your Mitsumi LU-005, - FX-001 or FX-001D CDROM drive. In addition, this driver uses much less - kernel memory than the old one, if that is a concern. This driver is - able to support more than one drive, but each drive needs a separate - interface card. Check out Documentation/cdrom/mcdx. + FX-001 or FX-001D CDROM drive. In addition, this driver uses much + less kernel memory than the old one, if that is a concern. This + driver is able to support more than one drive, but each drive needs + a separate interface card. Check out Documentation/cdrom/mcdx. Matsushita/Panasonic/Creative, Longshine, TEAC CDROM support CONFIG_SBPCD @@ -2913,7 +2896,7 @@ addresses and drive types; this can help to find facts in cases you are not sure, but can consume some time during the boot process if none of the supported drives gets found. - Once your drive got found, you should enter the reported parameters + Once your drive got found, you should enter the reported parameters into linux/include/linux/sbpcd.h and set "DISTRIBUTION 0" there. This driver can support up to four CDROM interface cards, and each card can support up to four CDROM drives; if you say Y here, you @@ -2938,7 +2921,8 @@ Sony CDU535 CDROM support CONFIG_CDU535 - This is the driver for the older Sony CDU-535 and CDU-531 CDROM drives. + This is the driver for the older Sony CDU-535 and CDU-531 CDROM + drives. Goldstar R420 CDROM support CONFIG_GSCD @@ -2958,7 +2942,7 @@ compatible interface. It also works with the Lasermate CR328A. If you have one of those, say Y. This driver does not work for the Optics Storage 8001 drive; use the IDE-ATAPI CDROM driver for that - one. + one. Sanyo CDR-H94A CDROM support CONFIG_SJCD @@ -2990,8 +2974,8 @@ The command line isp16=noisp16 will skip detection and configuration after all. - N.B. options are case sensitive. - Read Documentation/cdrom/isp16 for details. + N.B. options are case sensitive. + Read Documentation/cdrom/isp16 for details. Quota support CONFIG_QUOTA @@ -3077,7 +3061,7 @@ FAT support. This is not a filesystem in itself, but it provides the foundation for the other filesystems. This option will enlarge your kernel about 24 kB. If unsure, say Y. If you want to compile - this as a module however ( = code which can be inserted in and + this as a module however ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. Note that if you compile the FAT support as a module, you cannot compile any of the FAT-based file- @@ -3114,16 +3098,16 @@ vfat fs support CONFIG_VFAT_FS This allows you to mount MSDOS partitions of your harddrive. It - will let you use filenames in a way compatible with the long + will let you use filenames in a way compatible with the long filenames used by Windows'95 and Windows NT fat-based (not NTFS) partitions. It does not support Windows'95 compressed filesystems. You cannot use the VFAT filesystem for your root partition; use UMSDOS instead. This option enlarges your kernel by about 10 kB and it only works if you enabled the "fat fs support" above. Please read the file Documentation/filesystems/vfat.txt for details. - If unsure, say N. If you want to compile this 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. + If unsure, say N. If you want to compile this 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. umsdos: Unix like fs on top of std MSDOS fs CONFIG_UMSDOS_FS @@ -3146,6 +3130,158 @@ Documentation/modules.txt. Note that the filesystem of your root partition cannot be a module. +nls: Native language codepages and Unicode support +CONFIG_NLS + This is required by the FAT based filesystems and by the ISO9660 + filesystem with Joliet support. Joliet is a Microsoft extension + for CDROMs that supports Unicode. + This allows translation between different character sets. When + dealing with the FAT based filesystems, there are two character + sets that are important. The first is the codepage. Codepages are + character sets that are used by DOS to allow filenames to have + native language characters when character sets were limited to + 256 characters. The codepage is the character set that is used to + store native language characters on disk. + The two most common codepages are 437 in the United States and 850 + in much of Europe. The second important character set is the + input/output character set. This is the character set that is + displayed on the screen. In the United States, this will almost + always be the ISO 8859-1 character set. This is the default. Linux + will only do a translation of the FAT filenames, not the contents + of the files. + +nls iso8859-1 +CONFIG_NLS_ISO8859_1 + ISO8859-1 is the Latin 1 character set, and it covers most West + European languages such as Albanian, Catalan, Danish, Dutch, + English, Faeroese, Finnish, French, German, Galician, Irish, + Icelandic, Italian, Norwegian, Portuguese, Spanish, Swedish, and + Valencian. + +nls iso8859-2 +CONFIG_NLS_ISO8859_2 + ISO8859-2 is the Latin 2 character set, and it works for most + Latin-written Slavic and Central European languages: Czech, German, + Hungarian, Polish, Rumanian, Croatian, Slovak, Slovene. + +nls iso8859-3 +CONFIG_NLS_ISO8859_3 + ISO8859-3 is the Latin 3 character set, and it s popular with + authors of Esperanto, Galician, Maltese, and Turkish. + +nls iso8859-4 +CONFIG_NLS_ISO8859_4 + ISO8859-4 is the Latin 4 character set, and it introduces letters + for Estonian, Latvian, and Lithuanian. It is an incomplete + predecessor of Latin 6. + +nls iso8859-5 +CONFIG_NLS_ISO8859_5 + ISO8859-5 is a Cyrillic character set, and you can type Bulgarian, + Byelorussian, Macedonian, Russian, Serbian, and Ukrainian. + Note that the charset KOI8-R is preferred in Russia. + +nls iso8859-6 +CONFIG_NLS_ISO8859_6 + ISO8859-6 is the Arabic character set. + +nls iso8859-7 +CONFIG_NLS_ISO8859_7 + ISO8859-7 is the Modern Greek character set. + +nls iso8859-8 +CONFIG_NLS_ISO8859_8 + ISO8859-8 is the Hebrew character set. + +nls iso8859-9 +CONFIG_NLS_ISO8859_9 + ISO8859-9 is the Latin 5 character set, and it replaces the rarely + needed Icelandic letters in Latin 1 with the Turkish ones. + Useful in Turkey. + +nls iso8859-10 +CONFIG_NLS_ISO8859_10 + ISO8859-10 is the Latin 6 character set, and it adds the last + Inuit (Greenlandic) and Sami (Lappish) letters that were missing + in Latin 4 to cover the entire Nordic area. + +nls koi8-r +CONFIG_NLS_KOI8_R + This is the preferred Russian character set. + +nls codepage 437 +CONFIG_NLS_CODEPAGE_437 + This is the DOS codepage that is used in the United States and parts + of Canada. + +nls codepage 737 +CONFIG_NLS_CODEPAGE_737 + This is the codepage used by DOS for Greek. + +nls codepage 775 +CONFIG_NLS_CODEPAGE_775 + This is the codepage used by DOS for the Baltic Rim Languages. + +nls codepage 850 +CONFIG_NLS_CODEPAGE_850 + This is the DOS codepage that is used in much of Europe--United + Kingdom, Germany, Spain, Italy, and [add more countries here]. + It has some characters useful to many European languages that are + not part of codepage 437. + +nls codepage 852 +CONFIG_NLS_CODEPAGE_852 + This is the Latin 2 codepage used by DOS for much of Central and + Eastern Europe. It has all the required characters for these + languages: Albanian, Croatian, Czech, English, Finnish, Hungarian, + Irish, German, Polish, Romanian, Serbian (Latin transcription), + Slovak, Slovenian, and Sorbian. + +nls codepage 855 +CONFIG_NLS_CODEPAGE_855 + This is the DOS codepage that is used for Cyrillic. + +nls codepage 857 +CONFIG_NLS_CODEPAGE_857 + This is the DOS codepage that is used for Turkish. + +nls codepage 860 +CONFIG_NLS_CODEPAGE_860 + This is the DOS codepage that is used for Portuguese. + +nls codepage 861 +CONFIG_NLS_CODEPAGE_861 + This is the DOS codepage that is used for Icelandic. + +nls codepage 862 +CONFIG_NLS_CODEPAGE_862 + This is the DOS codepage that is used for Hebrew. + +nls codepage 863 +CONFIG_NLS_CODEPAGE_863 + This is the DOS codepage that is used for Canadian French. + +nls codepage 864 +CONFIG_NLS_CODEPAGE_864 + This is the DOS codepage that is used for Arabic. + +nls codepage 865 +CONFIG_NLS_CODEPAGE_865 + This is the DOS codepage that is used in the Nordic European + countries. + +nls codepage 866 +CONFIG_NLS_CODEPAGE_866 + This is the DOS codepage that is used for Cyrillic/Russian. + +nls codepage 869 +CONFIG_NLS_CODEPAGE_869 + This is the DOS codepage that is used for Greek. + +nls codepage 874 +CONFIG_NLS_CODEPAGE_874 + This is the DOS codepage that is used for Thai. + /proc filesystem support CONFIG_PROC_FS This is a virtual filesystem providing information about the status @@ -3163,7 +3299,7 @@ source of trouble if two devices are mistakenly configured to use the same IRQ). Several programs depend on this, so everyone should say Y here. - + NFS filesystem support CONFIG_NFS_FS If you are connected to some other (usually local) Unix computer @@ -3245,7 +3381,7 @@ inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read Documentation/modules.txt. If unsure, say N. - + System V and Coherent filesystem support CONFIG_SYSV_FS SCO, Xenix and Coherent are commercial Unix systems for intel @@ -3318,12 +3454,12 @@ SMD disklabel (Sun partition tables) support CONFIG_SMD_DISKLABEL Like most systems, SunOS uses its own partition table format, - incompatible with all others. Enabling this option allows you to read - these partition tables and further mount SunOS disks on your Linux - box if you also have configured BSD ufs filesystem support. This is - mainly used to carry data from a Sparc under SunOS to your Linux box - via a removable medium like magneto-optical or ZIP drives. If you - don't know what all this is about, say N. + incompatible with all others. Enabling this option allows you to + read these partition tables and further mount SunOS disks on your + Linux box if you also have configured BSD ufs filesystem support. + This is mainly used to carry data from a Sparc under SunOS to your + Linux box via a removable medium like magneto-optical or ZIP drives. + If you don't know what all this is about, say N. SMB filesystem support (to mount WfW shares etc..) CONFIG_SMB_FS @@ -3367,20 +3503,21 @@ Amiga FFS filesystem support (EXPERIMENTAL) CONFIG_AFFS_FS - The Fast File System (FFS) is the common filesystem used on harddisks - by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20). It's also - possible to mount diskfiles used by the Un*X Amiga Emulator by Bernd - Schmidt (http://www-users.informatik.rwth-aachen.de/~crux/uae.html) + The Fast File System (FFS) is the common filesystem used on + harddisks by Amiga (tm) Systems since AmigaOS Version 1.3 (34.20). + It's also possible to mount diskfiles used by + the Un*X Amiga Emulator by Bernd Schmidt + (http://www-users.informatik.rwth-aachen.de/~crux/uae.html). If you want to do the latter, you will also need the loop device - support. Say Y if you want to be able to read and write files from - and to an Amiga FFS partition of your harddrive. Amiga floppies + support. Say Y if you want to be able to read and write files from + and to an Amiga FFS partition of your harddrive. Amiga floppies however cannot be read with this driver due to an incompatibility of the floppy controller used in an Amiga and the standard floppy - controller in PCs and workstations. Read - Documentation/filesystems/affs.txt. This filesystem is also available - as a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. + controller in PCs and workstations. Read + Documentation/filesystems/affs.txt. This filesystem is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. If unsure, say N. Standard/generic serial support @@ -3428,7 +3565,7 @@ safe to say N. (As of 1.3.9x kernels, this driver's minor numbers start at 0 instead of 32.) -Stallion multiport serial support +Stallion multiport serial support CONFIG_STALDRV Stallion cards give you many serial ports. You would need something like this to connect more than two modems to your linux box, for @@ -3436,8 +3573,8 @@ asked for your specific card model in the next questions. Make sure to read drivers/char/README.stallion in this case. If you have never heard about all this, it's safe to say N. - -Stallion EasyIO or EC8/32 support + +Stallion EasyIO or EC8/32 support CONFIG_STALLION n If you have an EasyIO or EasyConnection 8/32 multiport Stallion card, then this is for you; say Y. Make sure to read @@ -3508,7 +3645,7 @@ 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. If you are unsure, say N and read the - HOWTO nevertheless: it will tell you what you have. + HOWTO nevertheless: it will tell you what you have. PS/2 mouse (aka "auxiliary device") support CONFIG_PSMOUSE @@ -3556,7 +3693,7 @@ compile this 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. If you are unsure, say N and read - the HOWTO nevertheless: it will tell you what you have. + the HOWTO nevertheless: it will tell you what you have. Support for user miscellaneous modules CONFIG_UMISC @@ -3629,26 +3766,26 @@ CONFIG_APM_DO_ENABLE Enable APM features at boot time. From page 36 of the APM BIOS specification: "When disabled, the APM BIOS does not automatically - power manage devices, enter the Standby State, enter the Suspend State, - or take power saving steps in response to CPU Idle calls." This driver - will make CPU Idle calls when Linux is idle (unless this feature is - turned off -- see below). This should always save battery power, but - more complicated APM features will be dependent on your BIOS - implementation. You may need to turn this option off if your computer - hangs at boot time when using APM support, or if it beeps continuously - instead of suspending. Turn this off if you have a NEC UltraLite Versa - 33/C or a Toshiba T400CDT. This is off by default since most machines - do fine without this feature. + power manage devices, enter the Standby State, enter the Suspend + State, or take power saving steps in response to CPU Idle calls." + This driver will make CPU Idle calls when Linux is idle (unless this + feature is turned off -- see below). This should always save + battery power, but more complicated APM features will be dependent + on your BIOS implementation. You may need to turn this option off + if your computer hangs at boot time when using APM support, or if it + beeps continuously instead of suspending. Turn this off if you have + a NEC UltraLite Versa 33/C or a Toshiba T400CDT. This is off by + default since most machines do fine without this feature. Do CPU IDLE calls CONFIG_APM_CPU_IDLE Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop. - On some machines, this can activate improved power savings, such as a - slowed CPU clock rate, when the machine is idle. These idle calls are - made after the idle loop has run for some length of time (e.g., 333 - mS). On some machines, this will cause a hang at boot time or whenever - the CPU becomes idle. (On machines with more than one CPU, this option - does nothing.) + On some machines, this can activate improved power savings, such as + a slowed CPU clock rate, when the machine is idle. These idle calls + are made after the idle loop has run for some length of time (e.g., + 333 ms). On some machines, this will cause a hang at boot time or + whenever the CPU becomes idle. (On machines with more than one CPU, + this option does nothing.) Enable console blanking using APM CONFIG_APM_DISPLAY_BLANK @@ -3657,17 +3794,25 @@ screen. Note that this is only used by the VC screen blanker, and won't turn off the backlight when using X11 (this also doesn't have anything to do with your VESA-compliant power-saving monitor). - Further, this option doesn't work for all laptops -- it might not turn - off your backlight at all, or it might print a lot of errors to the - console, especially if you are using gpm. + Further, this option doesn't work for all laptops---it might not + turn off your backlight at all, or it might print a lot of errors to + the console, especially if you are using gpm. -Power off on shutdown +Power off on shutdown CONFIG_APM_POWER_OFF - This option will power off the computer after the Linux kernel is halted - (e.g., with the halt(8) command). As with the other APM options, this - option may not work reliably with some APM BIOS implementations. + This option will power off the computer after the Linux kernel is + halted (e.g., with the halt(8) command). As with the other APM + options, this option may not work reliably with some APM BIOS + implementations. + +Ignore multiple suspend/standby events +CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + This option is necessary on the Thinkpad 560, but should work on all + other laptops. When the APM BIOS returns multiple suspend or standby + events while one is already being processed they will be ignored. + Without this the Thinkpad 560 has troubles with apmd, and pcmcia-cs. -Watchdog Timer Support +Watchdog Timer Support CONFIG_WATCHDOG If you enable this option and create a character special file /dev/watchdog with major number 10 and minor number 130 using mknod @@ -3713,8 +3858,8 @@ Fan Tachometer CONFIG_WDT_501_FAN - Enable the Fan Tachometer on the WDT501. Only do this if you have a fan - tachometer actually set up. + Enable the Fan Tachometer on the WDT501. Only do this if you have + a fan tachometer actually set up. Software Watchdog CONFIG_SOFT_WATCHDOG @@ -3764,11 +3909,11 @@ information in various README files in drivers/sound. If you want to compile this 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. I'm told that even without a sound - card, you can make your computer say more than an occasional beep, - by programming the PC speaker. Kernel patches and programs to do - that are at - sunsite.unc.edu:/pub/Linux/kernel/patches/console/pcsndrv-X.X.tar.gz, + read Documentation/modules.txt. + I'm told that even without a sound card, you can make your computer + say more than an occasional beep, by programming the PC speaker. + Kernel patches and programs to do that are at sunsite.unc.edu: + /pub/Linux/kernel/patches/console/pcsndrv-X.X.tar.gz, to be extracted with "tar xzvf filename". ProAudioSpectrum 16 support @@ -3884,14 +4029,9 @@ Support for Turtle Beach Wave Front (Maui, Tropez) synthesizers CONFIG_MAUI - Enable this option if you have a Turtle Beach Wave Front, Maui, or + Enable this option if you have a Turtle Beach Wave Front, Maui, or Tropez sound card. -Support for Crystal CS4232 based (PnP) cards -CONFIG_CS4232 - Use this option to enable experimental support for cards that use - the Plug and Play protocol. - /dev/dsp and /dev/audio support CONFIG_AUDIO Answering N disables /dev/dsp and /dev/audio, the A/D and D/A @@ -3923,7 +4063,7 @@ become a kernel hacker, you can start with the Kernel Hacker's Guide, available via ftp (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/LDP. Mere mortals say N. - + Profile shift count CONFIG_PROFILE_SHIFT This is used to adjust the granularity with which the addresses of @@ -3977,7 +4117,7 @@ (mgetty+sendfax by gert@greenie.muc.de with an extension, available with the ISDN utility package for example), you will be able to use your Linux box as an ISDN-answering machine. Of course, this - must be supported by the lowlevel driver also. Currently HiSax + must be supported by the lowlevel driver also. Currently HiSax driver is the only voice-supporting drivers. See Documentation/isdn/README.audio for more information. @@ -3996,8 +4136,8 @@ This driver replaces the old Teles driver. It supports the Siemens chipset in a more general way. This chipset is used on various ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, - Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and - many compatibles). It's a complete rewrite of the original Teles + Teles S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and + many compatibles). It's a complete rewrite of the original Teles driver. See Documentation/isdn/README.HiSax for further informations on using this driver. @@ -4006,7 +4146,7 @@ CONFIG_HISAX_16_0 This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8 and many compatibles. - See Documentation/isdn/README.HiSax on how to configure it + See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or non-standard irq/port/shmem settings. @@ -4014,7 +4154,7 @@ CONFIG_HISAX_16_3 This enables HiSax support for the Teles ISDN-cards S0-16.3 the Teles/Creatix PnP and the Teles PCMCIA. - See Documentation/isdn/README.HiSax on how to configure it + See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or non-standard irq/port/shmem settings. @@ -4031,7 +4171,7 @@ for the Elsa Quickstep series cards for the ISA bus. You don't have to select "HiSax Support for Elsa PCMCIA card" at the same time. - See Documentation/isdn/README.HiSax on how to configure it + See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or non-standard irq/port/shmem settings. @@ -4040,14 +4180,14 @@ This enables HiSax support for the Elsa PCMCIA card. You don't have to select "HiSax Support for Elsa ISA cards" at the same time. - See Documentation/isdn/README.HiSax on how to configure it + See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or non-standard irq/port/shmem settings. HiSax Support for ITK ix1-micro Revision 2 CONFIG_HISAX_IX1MICROR2 This enables HiSax support for the ITK ix1-micro Revision 2 card. - See Documentation/isdn/README.HiSax on how to configure it + See Documentation/isdn/README.HiSax on how to configure it using the different cards, a different D-channel protocol, or non-standard irq/port/shmem settings. diff -u --recursive --new-file v2.0.33/linux/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd --- v2.0.33/linux/Documentation/cdrom/ide-cd Wed Sep 25 01:12:11 1996 +++ linux/Documentation/cdrom/ide-cd Wed Jun 3 15:17:46 1998 @@ -277,7 +277,16 @@ there are hardware problems with the interrupt setup; they apparently don't use interrupts. - + - If you own a Pioneer DR-A24X, you _will_ get nasty error messages + on boot such as "irq timeout: status=0x50 { DriveReady SeekComplete }" + The Pioneer DR-A24X cdrom drives are fairly popular these days. + Unfortunatly, these drives seem to become very confused when we perform + the standard Linux ATA disk drive probe. If you own one of these drives, + you can bypass the ATA probing which confuses these cdrom drives, by + adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and runing + lilo (again where X is the drive letter corresponding to where your drive + is installed (see section 2)) + c. System hangups. - If the system locks up when you try to access the cdrom, the most diff -u --recursive --new-file v2.0.33/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.0.33/linux/Documentation/devices.txt Wed Jul 10 03:11:44 1996 +++ linux/Documentation/devices.txt Wed Jun 3 15:17:46 1998 @@ -78,10 +78,14 @@ the position within the series. block Floppy disks - 0 = /dev/fd0 First floppy disk autodetect - 1 = /dev/fd1 Second floppy disk autodetect - 2 = /dev/fd2 Third floppy disk autodetect - 3 = /dev/fd3 Fourth floppy disk autodetect + 0 = /dev/fd0 Controller 1, drive 1 autodetect + 1 = /dev/fd1 Controller 1, drive 2 autodetect + 2 = /dev/fd2 Controller 1, drive 3 autodetect + 3 = /dev/fd3 Controller 1, drive 4 autodetect + 128 = /dev/fd4 Controller 2, drive 1 autodetect + 129 = /dev/fd5 Controller 2, drive 2 autodetect + 130 = /dev/fd6 Controller 2, drive 3 autodetect + 131 = /dev/fd7 Controller 2, drive 4 autodetect To specify format, add to the autodetect device number: 0 = /dev/fd? Autodetect format diff -u --recursive --new-file v2.0.33/linux/Documentation/filesystems/00-INDEX linux/Documentation/filesystems/00-INDEX --- v2.0.33/linux/Documentation/filesystems/00-INDEX Sat Nov 30 02:21:18 1996 +++ linux/Documentation/filesystems/00-INDEX Wed Jun 3 15:17:46 1998 @@ -4,6 +4,8 @@ - info and mount options for the Amiga Fast File System. hpfs.txt - info and mount options for the OS/2 HPFS. +isofs.txt + - info and mount options for the ISO9660 (CDROM) filesystem. ncpfs.txt - info on Novell Netware(tm) filesystem using NCP protocol. smbfs.txt diff -u --recursive --new-file v2.0.33/linux/Documentation/filesystems/isofs.txt linux/Documentation/filesystems/isofs.txt --- v2.0.33/linux/Documentation/filesystems/isofs.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/filesystems/isofs.txt Wed Jun 3 15:17:46 1998 @@ -0,0 +1,31 @@ +Mount options that are the same as for msdos partitions. + + uid=nnn All files in the partition will be owned by user id nnn. + gid=nnn All files in the partition will be in group nnn. + umask=nnn The permission mask (see umask(1)) for the partition. + conv=binary Data is returned exactly as is, with CRLF's. [default] + conv=text (Carriage return, line feed) is replaced with newline. + conv=mtext (Carriage return, line feed) is returned as is (?). + conv=auto Chooses, file by file, conv=binary or conv=text (by guessing) + +Mount options that are the same as vfat partitions. These are only useful +when using discs encoded using Microsoft's Joliet extensions. + iocharset=name Character set to use for converting from Unicode to + ASCII. Joliet filenames are stored in Unicode format, but + Unix for the most part doesn't know how to deal with Unicode. + There is also an option of doing UTF8 translations with the + utf8 option. + utf8 Encode Unicode names in UTF8 format. Default is no. + +Mount options that are unique to the isofs filesystem. + nojoliet Ignore Joliet extensions if they are present. + norock Ignore rockridge extensions if they are present. + unhide Show hidden files (?). + cruft Handle badly formatted CDs (?) + map=off Do not map non-rockridge filenames to lowercase + map=normal Map rockridge filenames to lowercase + check=relaxed + check=strict + block=512 Set the block size for the disk to 512 bytes + block=1024 Set the block size for the disk to 1024 bytes + block=2048 Set the block size for the disk to 2048 bytes diff -u --recursive --new-file v2.0.33/linux/Documentation/filesystems/vfat.txt linux/Documentation/filesystems/vfat.txt --- v2.0.33/linux/Documentation/filesystems/vfat.txt Thu Apr 11 23:49:29 1996 +++ linux/Documentation/filesystems/vfat.txt Wed Jun 3 15:17:46 1998 @@ -8,6 +8,19 @@ VFAT MOUNT OPTIONS ---------------------------------------------------------------------- +codepage=### -- Sets the codepage for converting to shortname characters + on FAT and VFAT filesystems. By default, codepage 437 + is used. This is the default for the U.S. and some + European countries. +iocharset=name-- Character set to use for converting between 8 bit characters + and 16 bit Unicode characters. Long filenames are stored on + disk in Unicode format, but Unix for the most part doesn't + know how to deal with Unicode. There is also an option of + doing UTF8 translations with the utf8 option. +utf8 -- UTF8 is the filesystem safe version of Unicode that + is used by the console. It can be be enabled for the + filesystem with this option. If 'uni_xlate' gets set, + UTF8 gets disabled. uni_xlate -- Translate unhandled Unicode characters to special escaped sequences. This would let you backup and restore filenames that are created with any Unicode @@ -31,18 +44,59 @@ quiet -- Stops printing certain warning messages. +Explanation of Native Language Support in the VFAT Filesystem +---------------------------------------------------------------------- +There are two different character sets are needed by the vfat +filesystem. The first is the codepage character set. The codepage is +the character set that is used to store short filenames on disk. Its +mount option is 'codepage=437' which 437 is the codepage number. + +Long filenames are stored in Unicode, but since the Linux filesystem +doesn't deal with 16 bit characters, we need some way of converting +characters. There are a couple options of how to do this. One is to +use the 'utf8' mount option and I will cover that a bit later. The +other is to use the 'iocharset=iso8859-1' mount option where the +iso8859-1 tells the filesystem which character set is used for input +and output. If you are in Russia, you might specify koi8-r here. +If a Unicode character on disk cannot be mapped to anything in the +iocharset, it is replaced with a '?'. + +The iocharset is used to convert long filenames to and from Unicode. +It is currently implemented. The codepage is used to convert short +filenames to and from the iocharset. This translation is not currently +implemented. + +If no iocharset is specified and the default is unable to be loaded, +the mount will succeed while falling back to doing no conversions at +all. If a charset is explicity specified and the charset cannot be +loaded, the mount will fail. + +For the codepage, the default mount option is 'codepage=437'. If a +codepage is explicitly asked for and the load of the character set +fails, the mount will fail. Is no codepage is explicitly asked for +and the load of the character set fails, the load will still succeed. + +UTF8 is an 8 bit, filesystem safe representation of Unicode. It does +not lose any information in the conversion. However, you need to have +a terminal or a program that knows how to deal with UTF8. The Linux +console can be put into a mode where it will correctly display UTF8 +characters. I don't know if there is a similar mode for xterms, but +I don't believe there is. More information about UTF8 can be found +at http://www.unicode.com TODO ---------------------------------------------------------------------- -* Need to get rid of the raw scanning stuff. Instead, always use - a get next directory entry approach. The only thing left that uses - raw scanning is the directory renaming code. +* When only shortnames exist, translate them from the codepage character + set to the iocharset. Currently, translations only occur when longnames + exist. To translate, first convert from codepage to Unicode and then + to the output character set. * Need to add dcache_lookup code msdos filesystem. This means the directories need to be versioned like the vfat filesystem. -* Add support for different codepages. Right now, we only support - the a single English codepage. +* Need to get rid of the raw scanning stuff. Instead, always use + a get next directory entry approach. The only thing left that uses + raw scanning is the directory renaming code. * Fix the Posix filesystem support to work in 8.3 space. This involves renaming aliases if a conflict occurs between a new filename and @@ -55,6 +109,7 @@ * When a volume name is the same as a directory name in the root directory of the filesystem, the directory name sometimes shows up empty an empty file. +* autoconv option does not work correctly. BUG REPORTS ---------------------------------------------------------------------- @@ -194,271 +249,3 @@ Finally, note that the extended name is stored in Unicode. Each Unicode character takes two bytes. - - -NOTES ON UNICODE TRANSLATION IN VFAT FILESYSTEM ----------------------------------------------------------------------- -(Information provided by Steve Searle ) - -Char used as Char(s) used Char(s) used in Entries which have -filename in shortname longname slot been corrected -0x80 (128) 0x80 0xC7 -0x81 (129) 0x9A 0xFC -0x82 (130) 0x90 0xE9 E -0x83 (131) 0xB6 0xE2 E -0x84 (132) 0x8E 0xE4 E -0x85 (133) 0xB7 0xE0 E -0x86 (134) 0x8F 0xE5 E -0x87 (135) 0x80 0xE7 E -0x88 (136) 0xD2 0xEA E -0x89 (137) 0xD3 0xEB E -0x8A (138) 0xD4 0xE8 E -0x8B (139) 0xD8 0xEF E -0x8C (140) 0xD7 0xEE E -0x8D (141) 0xDE 0xEC E -0x8E (142) 0x8E 0xC4 E -0x8F (143) 0x8F 0xC5 E -0x90 (144) 0x90 0xC9 E -0x91 (145) 0x92 0xE6 E -0x92 (146) 0x92 0xC6 E -0x93 (147) 0xE2 0xF4 E -0x94 (148) 0x99 0xF6 -0x95 (149) 0xE3 0xF2 -0x96 (150) 0xEA 0xFB -0x97 (151) 0xEB 0xF9 -0x98 (152) "_~1" 0xFF -0x99 (153) 0x99 0xD6 -0x9A (154) 0x9A 0xDC -0x9B (155) 0x9D 0xF8 -0x9C (156) 0x9C 0xA3 -0x9D (157) 0x9D 0xD8 -0x9E (158) 0x9E 0xD7 -0x9F (159) 0x9F 0x92 -0xA0 (160) 0xB5 0xE1 -0xA1 (161) 0xD6 0xE0 -0xA2 (162) 0xE0 0xF3 -0xA3 (163) 0xE9 0xFA -0xA4 (164) 0xA5 0xF1 -0xA5 (165) 0xA5 0xD1 -0xA6 (166) 0xA6 0xAA -0xA7 (167) 0xA7 0xBA -0xA8 (168) 0xA8 0xBF -0xA9 (169) 0xA9 0xAE -0xAA (170) 0xAA 0xAC -0xAB (171) 0xAB 0xBD -0xAC (172) 0xAC 0xBC -0xAD (173) 0xAD 0xA1 -0xAE (174) 0xAE 0xAB -0xAF (175) 0xAF 0xBB -0xB0 (176) 0xB0 0x91 0x25 -0xB1 (177) 0xB1 0x92 0x25 -0xB2 (178) 0xB2 0x93 0x25 -0xB3 (179) 0xB3 0x02 0x25 -0xB4 (180) 0xB4 0x24 0x25 -0xB5 (181) 0xB5 0xC1 -0xB6 (182) 0xB6 0xC2 -0xB7 (183) 0xB7 0xC0 -0xB8 (184) 0xB8 0xA9 -0xB9 (185) 0xB9 0x63 0x25 -0xBA (186) 0xBA 0x51 0x25 -0xBB (187) 0xBB 0x57 0x25 -0xBC (188) 0xBC 0x5D 0x25 -0xBD (189) 0xBD 0xA2 -0xBE (190) 0xBE 0xA5 -0xBF (191) 0xBF 0x10 0x25 -0xC0 (192) 0xC0 0x14 0x25 -0xC1 (193) 0xC1 0x34 0x25 -0xC2 (194) 0xC2 0x2C 0x25 -0xC3 (195) 0xC3 0x1C 0x25 -0xC4 (196) 0xC4 0x00 0x25 -0xC5 (197) 0xC5 0x3C 0x25 -0xC6 (198) 0xC7 0xE3 E -0xC7 (199) 0xC7 0xC3 -0xC8 (200) 0xC8 0x5A 0x25 E -0xC9 (201) 0xC9 0x54 0x25 E -0xCA (202) 0xCA 0x69 0x25 E -0xCB (203) 0xCB 0x66 0x25 E -0xCC (204) 0xCC 0x60 0x25 E -0xCD (205) 0xCD 0x50 0x25 E -0xCE (206) 0xCE 0x6C 0x25 E -0xCF (207) 0xCF 0xA4 E -0xD0 (208) 0xD1 0xF0 -0xD1 (209) 0xD1 0xD0 -0xD2 (210) 0xD2 0xCA -0xD3 (211) 0xD3 0xCB -0xD4 (212) 0xD4 0xC8 -0xD5 (213) 0x49 0x31 0x01 -0xD6 (214) 0xD6 0xCD -0xD7 (215) 0xD7 0xCE -0xD8 (216) 0xD8 0xCF -0xD9 (217) 0xD9 0x18 0x25 -0xDA (218) 0xDA 0x0C 0x25 -0xDB (219) 0xDB 0x88 0x25 -0xDC (220) 0xDC 0x84 0x25 -0xDD (221) 0xDD 0xA6 -0xDE (222) 0xDE 0xCC -0xDF (223) 0xDF 0x80 0x25 -0xE0 (224) 0xE0 0xD3 -0xE1 (225) 0xE1 0xDF -0xE2 (226) 0xE2 0xD4 -0xE3 (227) 0xE3 0xD2 -0xE4 (228) 0x05 0xF5 -0xE5 (229) 0x05 0xD5 -0xE6 (230) 0xE6 0xB5 -0xE7 (231) 0xE8 0xFE -0xE8 (232) 0xE8 0xDE -0xE9 (233) 0xE9 0xDA -0xEA (234) 0xEA 0xDB -0xEB (235) 0xEB 0xD9 -0xEC (236) 0xED 0xFD -0xED (237) 0xED 0xDD -0xEE (238) 0xEE 0xAF -0xEF (239) 0xEF 0xB4 -0xF0 (240) 0xF0 0xAD -0xF1 (241) 0xF1 0xB1 -0xF2 (242) 0xF2 0x17 0x20 -0xF3 (243) 0xF3 0xBE -0xF4 (244) 0xF4 0xB6 -0xF5 (245) 0xF5 0xA7 -0xF6 (246) 0xF6 0xF7 -0xF7 (247) 0xF7 0xB8 -0xF8 (248) 0xF8 0xB0 -0xF9 (249) 0xF9 0xA8 -0xFA (250) 0xFA 0xB7 -0xFB (251) 0xFB 0xB9 -0xFC (252) 0xFC 0xB3 -0xFD (253) 0xFD 0xB2 -0xFE (254) 0xFE 0xA0 0x25 -0xFF (255) 0xFF 0xA0 - - -Page 0 -0x80 (128) 0x00 -0x81 (129) 0x00 -0x82 (130) 0x00 -0x83 (131) 0x00 -0x84 (132) 0x00 -0x85 (133) 0x00 -0x86 (134) 0x00 -0x87 (135) 0x00 -0x88 (136) 0x00 -0x89 (137) 0x00 -0x8A (138) 0x00 -0x8B (139) 0x00 -0x8C (140) 0x00 -0x8D (141) 0x00 -0x8E (142) 0x00 -0x8F (143) 0x00 -0x90 (144) 0x00 -0x91 (145) 0x00 -0x92 (146) 0x00 -0x93 (147) 0x00 -0x94 (148) 0x00 -0x95 (149) 0x00 -0x96 (150) 0x00 -0x97 (151) 0x00 -0x98 (152) 0x00 -0x99 (153) 0x00 -0x9A (154) 0x00 -0x9B (155) 0x00 -0x9C (156) 0x00 -0x9D (157) 0x00 -0x9E (158) 0x00 -0x9F (159) 0x92 -0xA0 (160) 0xFF -0xA1 (161) 0xAD -0xA2 (162) 0xBD -0xA3 (163) 0x9C -0xA4 (164) 0xCF -0xA5 (165) 0xBE -0xA6 (166) 0xDD -0xA7 (167) 0xF5 -0xA8 (168) 0xF9 -0xA9 (169) 0xB8 -0xAA (170) 0x00 -0xAB (171) 0xAE -0xAC (172) 0xAA -0xAD (173) 0xF0 -0xAE (174) 0x00 -0xAF (175) 0xEE -0xB0 (176) 0xF8 -0xB1 (177) 0xF1 -0xB2 (178) 0xFD -0xB3 (179) 0xFC -0xB4 (180) 0xEF -0xB5 (181) 0xE6 -0xB6 (182) 0xF4 -0xB7 (183) 0xFA -0xB8 (184) 0xF7 -0xB9 (185) 0xFB -0xBA (186) 0x00 -0xBB (187) 0xAF -0xBC (188) 0xAC -0xBD (189) 0xAB -0xBE (190) 0xF3 -0xBF (191) 0x00 -0xC0 (192) 0xB7 -0xC1 (193) 0xB5 -0xC2 (194) 0xB6 -0xC3 (195) 0xC7 -0xC4 (196) 0x8E -0xC5 (197) 0x8F -0xC6 (198) 0x92 -0xC7 (199) 0x80 -0xC8 (200) 0xD4 -0xC9 (201) 0x90 -0xCA (202) 0xD2 -0xCB (203) 0xD3 -0xCC (204) 0xDE -0xCD (205) 0xD6 -0xCE (206) 0xD7 -0xCF (207) 0xD8 -0xD0 (208) 0x00 -0xD1 (209) 0xA5 -0xD2 (210) 0xE3 -0xD3 (211) 0xE0 -0xD4 (212) 0xE2 -0xD5 (213) 0xE5 -0xD6 (214) 0x99 -0xD7 (215) 0x9E -0xD8 (216) 0x9D -0xD9 (217) 0xEB -0xDA (218) 0xE9 -0xDB (219) 0xEA -0xDC (220) 0x9A -0xDD (221) 0xED -0xDE (222) 0xE8 -0xDF (223) 0xE1 -0xE0 (224) 0x85, 0xA1 -0xE1 (225) 0xA0 -0xE2 (226) 0x83 -0xE3 (227) 0xC6 -0xE4 (228) 0x84 -0xE5 (229) 0x86 -0xE6 (230) 0x91 -0xE7 (231) 0x87 -0xE8 (232) 0x8A -0xE9 (233) 0x82 -0xEA (234) 0x88 -0xEB (235) 0x89 -0xEC (236) 0x8D -0xED (237) 0x00 -0xEE (238) 0x8C -0xEF (239) 0x8B -0xF0 (240) 0xD0 -0xF1 (241) 0xA4 -0xF2 (242) 0x95 -0xF3 (243) 0xA2 -0xF4 (244) 0x93 -0xF5 (245) 0xE4 -0xF6 (246) 0x94 -0xF7 (247) 0xF6 -0xF8 (248) 0x9B -0xF9 (249) 0x97 -0xFA (250) 0xA3 -0xFB (251) 0x96 -0xFC (252) 0x81 -0xFD (253) 0xEC -0xFE (254) 0xE7 -0xFF (255) 0x98 - diff -u --recursive --new-file v2.0.33/linux/Documentation/networking/depca.txt linux/Documentation/networking/depca.txt --- v2.0.33/linux/Documentation/networking/depca.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/depca.txt Wed Jun 3 15:17:46 1998 @@ -0,0 +1,92 @@ + +DE10x +===== + +Memory Addresses: + + SW1 SW2 SW3 SW4 +64K on on on on d0000 dbfff + off on on on c0000 cbfff + off off on on e0000 ebfff + +32K on on off on d8000 dbfff + off on off on c8000 cbfff + off off off on e8000 ebfff + +DBR ROM on on dc000 dffff + off on cc000 cffff + off off ec000 effff + +Note that the 2K mode is set by SW3/SW4 on/off or off/off. Address +assignment is through the RBSA register. + +I/O Address: + SW5 +0x300 on +0x200 off + +Remote Boot: + SW6 +Disable on +Enable off + +Remote Boot Timeout: + SW7 +2.5min on +30s off + +IRQ: + SW8 SW9 SW10 SW11 SW12 +2 on off off off off +3 off on off off off +4 off off on off off +5 off off off on off +7 off off off off on + +DE20x +===== + +Memory Size: + + SW3 SW4 +64K on on +32K off on +2K on off +2K off off + +Start Addresses: + + SW1 SW2 SW3 SW4 +64K on on on on c0000 cffff + on off on on d0000 dffff + off on on on e0000 effff + +32K on on off off c8000 cffff + on off off off d8000 dffff + off on off off e8000 effff + +Illegal off off - - - - + +I/O Address: + SW5 +0x300 on +0x200 off + +Remote Boot: + SW6 +Disable on +Enable off + +Remote Boot Timeout: + SW7 +2.5min on +30s off + +IRQ: + SW8 SW9 SW10 SW11 SW12 +5 on off off off off +9 off on off off off +10 off off on off off +11 off off off on off +15 off off off off on + diff -u --recursive --new-file v2.0.33/linux/Documentation/networking/ip_dynaddr.txt linux/Documentation/networking/ip_dynaddr.txt --- v2.0.33/linux/Documentation/networking/ip_dynaddr.txt Wed Sep 17 12:00:48 1997 +++ linux/Documentation/networking/ip_dynaddr.txt Wed Jun 3 15:17:46 1998 @@ -1,10 +1,10 @@ -IP dynamic address hack-port v0.03 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +IP dynamic address hack-port v0.03-rst +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This stuff allows diald ONESHOT connections to get established by dynamically changing packet source address (and socket's if local procs). It is implemented for TCP diald-box connections(1) and IP_MASQuerading(2). -If enabled[*] and forwarding interface has changed: +If enabled[*] and forwarding interface address has changed: 1) Socket (and packet) source address is rewritten ON RETRANSMISSIONS while in SYN_SENT state (diald-box processes). 2) Out-bounded MASQueraded source address changes ON OUTPUT (when @@ -16,14 +16,39 @@ going up. So, the *same* (local AND masqueraded) connections requests that bring the link up will be able to get established. +If you enable the RST-provoking mode, then the source address will +be changed, even if the socket is established. This means we send +an incorrect packet out, which causes the remote host to kill our +socket. This is the desired behaviour, because such a socket is +doomed anyway, and the earlier it dies, the better. This prevents +the dial-on-demand connection from being kept up by a dead connection, +and tells the application that the connection was lost. + [*] At boot, by default no address rewriting is attempted. - To enable: + +The values for the ip_dynaddr sysctl are: + + 1: To enable: + 2: To enable verbosity: + 4: To enable RST-provoking: + +Flags can be combined by adding them. Common settings +would be: + + To switch off special handling of dynamic addresses (default) + # echo 0 > /proc/sys/net/ipv4/ip_dynaddr + To enable rewriting in quiet mode: # echo 1 > /proc/sys/net/ipv4/ip_dynaddr - To enable verbose mode: + To enable rewriting in verbose mode: + # echo 3 > /proc/sys/net/ipv4/ip_dynaddr + (for backwards compatibility you can also use) # echo 2 > /proc/sys/net/ipv4/ip_dynaddr - To disable (default) - # echo 0 > /proc/sys/net/ipv4/ip_dynaddr + To enable quiet RST-provoking mode: + # echo 5 > /proc/sys/net/ipv4/ip_dynaddr + To enable verbose RST-provoking mode: + # echo 7 > /proc/sys/net/ipv4/ip_dynaddr Enjoy! -- Juanjo +(with RST-provoking mode by Erik Corry ) diff -u --recursive --new-file v2.0.33/linux/Documentation/networking/tlan.FAQ linux/Documentation/networking/tlan.FAQ --- v2.0.33/linux/Documentation/networking/tlan.FAQ Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/tlan.FAQ Wed Jun 3 15:17:46 1998 @@ -0,0 +1,28 @@ +1. Q. Ifconfig reports "10Mbs Ethernet" for my 10/100Mbs card. How do + I get my card to run in 100Mbs mode? + + A. The "10Mbs Ethernet" is poorly named. What this is really saying + is that this device is some kind of Ethernet device (ie, uses + ethernet style frames at the link layer). Recent versions of + ifconfig report this as simply "Ethernet". + + The TLAN driver is designed to autodetect 10Mbs vs. 100Mbs + connections, and choose the higher speed. The most sure way + to determine what speed you are running at is to look at the + 100Mbs LED on the card, if your device has one. + + +2. Q. My network card is using the same interrupt as my SCSI card. + + A. Some Compaqs set all PCI devices to the same interrupt by default. + You can either change the interrupt used by one of the device + with your Compaq configuration utility, or you can have the TLAN + driver use the same type of interrupt handler: + + a. For module based use, add 'sa_int=1' to the command line, eg.: + insmod tlan.o sa_int=1 + + b. For compiled in kernel, set the 0x2 bit in the third part + for the boot parameter for that device, eg.: + ether=0,0,0x2,0,eth0 + diff -u --recursive --new-file v2.0.33/linux/Documentation/networking/tlan.README linux/Documentation/networking/tlan.README --- v2.0.33/linux/Documentation/networking/tlan.README Wed Dec 31 16:00:00 1969 +++ linux/Documentation/networking/tlan.README Wed Jun 3 15:17:46 1998 @@ -0,0 +1,130 @@ +Caldera TLAN driver for Linux, version 0.42 +README + + +I. Supported Devices. + + Only PCI devices will work with this driver. + + Supported: + Vendor ID Device ID Name + 0e11 ae32 Compaq Netelligent 10/100 TX + 0e11 ae34 Compaq Netelligent 10 T + 0e11 ae35 Compaq Integrated NetFlex 3/P + 0e11 ae43 Compaq ProLiant Integrated Netelligent 10/100 TX + 0e11 ae40 Compaq Dual Port Netelligent 10/100 TX + 0e11 b011 Compaq Deskpro 4000 5233MMX + 0e11 f130 Compaq NetFlex 3/P + 0e11 f150 Compaq NetFlex 3/P + 108d 0014 Olicom OC-2326 + + Caveats: + + I don't believe 100BaseTX daughterboards will work. I am interested + in any reports. + + +II. Building the Driver. + + The TLAN driver may be compiled into the kernel, or it may be compiled + as a module separately, or in the kernel. A patch is included for + 2.0.29 (which also works for 2.0.30, 2.0.31, and 2.0.32). + + To compile it as part of the kernel: + 1. Download and untar the TLAN driver package. + 2. If your kernel is 2.1.45 or later, you do not need to patch the + kernel sources. Copy the tlan.c and tlan.h to drivers/net in + the kernel source tree. + 3. Otherwise, apply the appropriate patch for your kernel. For + example: + + cd /usr/src/linux + patch -p1 < kernel.2.0.29 + + 4. Copy the files tlan.c and tlan.h from the TLAN package to the + directory drivers/net in the Linux kernel source tree. + 5. Configure your kernel for the TLAN driver. Answer 'Y' when + prompted to ask about experimental code (the first question). + Then answer 'Y' when prompted if to include TI ThunderLAN + support. If you want the driver compiled as a module, answer 'M' + instead of 'Y'. + 6. Make the kernel and, if necessary, the modules. + + To compile the TLAN driver independently: + 1. Download and untar the TLAN driver package. + 2. Change to the tlan directory. + 3. If you are NOT using a versioned kernel (ie, want an non- + versioned module), edit the Makefile, and comment out the + line: + MODVERSIONS = -DMODVERSIONS + 4. Run 'make'. + + +III. Driver Options + 1. You can append debug=x to the end of the insmod line to get + debug messages, where x is a bit field where the bits mean + the following: + + 0x01 Turn on general debugging messages. + 0x02 Turn on receive debugging messages. + 0x04 Turn on transmit debugging messages. + 0x08 Turn on list debugging messsages. + + 2. You can append aui=1 to the end of the insmod line to cause + the adapter to use the AUI interface instead of the 10 Base T + interface. This is also what to do if you want to use the BNC + connector on a TLAN based device. (Setting this option on a + device that does not have an AUI/BNC connector will probably + cause it to not function correctly.) + + 3. If the driver is built into the kernel, you can use the 3rd + and 4th parameters to set aui and debug respectively. For + example: + + ether=0,0,0x1,0x7,eth0 + + This sets aui to 0x1 and debug to 0x7, assuming eth0 is a + supported TLAN device. + + The bits in the third byte are assigned as follows: + + 0x01 = aui + 0x02 = use SA_INTERRUPT flag when reserving the irq. + + +IV. Things to try if you have problems. + 1. Make sure your card's PCI id is among those listed in + section I, above. + 1. Make sure routing is correct. + 2. If you are using a 2.1.x kernel, try to duplicate the + problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel. + 3. Set debug to 7, either in tlan.c or through insmod as in + section III.1 above. + 4. Make sure klog is running so the kernel messages are + being recorded somewhere. + 5. Run the following sequence of programs in order (you + may want to do this within an xterm, as background + traffic may cause a lot of TLAN RECEIVED: messages + on the console): + + ifconfig eth0 your.ip.address netmask your.net.mask up + route add -net local.net.address eth0 + ifconfig + ping some.computer.on.local.net + ifconfig eth0 down + + 6. Mail the log of what occurred to me. Also include the + kernel version and what media/connector type (eg, + 10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc). + + + +Please e-mail me with any comments, successes, or failures. Thanks. + +There is also a tlan mailing list which you can join by sending "subscribe tlan" +in the body of an email to majordomo@vuser.vu.union.edu. I will announce new +releases of the TLAN driver there. + +James +james.banks@caldera.com + diff -u --recursive --new-file v2.0.33/linux/Documentation/riscom8.txt linux/Documentation/riscom8.txt --- v2.0.33/linux/Documentation/riscom8.txt Fri Apr 26 02:12:36 1996 +++ linux/Documentation/riscom8.txt Wed Jun 3 15:17:46 1998 @@ -1,5 +1,5 @@ This is the README for RISCom/8 multi-port serial driver - (C) 1994-1996 D.Gorodchanin (begemot@bgm.rosrpint.net) + (C) 1994-1996 D.Gorodchanin (pgmdsg@ibi.com) See file LICENSE for terms and conditions. NOTE: English is not my native language. diff -u --recursive --new-file v2.0.33/linux/Documentation/rtc.txt linux/Documentation/rtc.txt --- v2.0.33/linux/Documentation/rtc.txt Mon May 6 22:18:50 1996 +++ linux/Documentation/rtc.txt Wed Jun 3 15:17:46 1998 @@ -65,6 +65,35 @@ Paul Gortmaker +Update in version 1.09 +====================== + +Epoch handling is added. Epoch is the number which should be added to the +value of the clock's year register to get the actual year. The default +Linux epoch is therefore 1900. + +Epochs are especially useful on Alphas where different operating systems +use different epochs, and Linux wants to be compatible with all of them. +They may eventually be helpful on Intel architecture as well, where a +value of an RTC register cannot exceed 99 due to BCD tradition originated +from DOS. + +When the epoch is set to 1900, the new code behaves exactly like the old +one with respect to the BCD wrapping: values 00 - 69 are treated as if +they were 100 - 169. That means that after the 2000th year epoch 1900 +will be the same as epoch 2000. + +Two new ioctls are introduced to read and set the epoch, RTC_EPOCH_READ +and RTC_EPOCH_SET. They can be used in exactly the same manner as +RTC_IRQP_READ and RTC_IRQP_SET, so they are not included in the example +program below. + +On Alphas an epoch autodetection is performed. Currently 3 epochs +are recognised: Linux (1900), Digital UNIX (1952) and Windows NT (1980). + +Nikita Schmidt + + -------------------- 8< ---------------- 8< ----------------------------- /* diff -u --recursive --new-file v2.0.33/linux/MAINTAINERS linux/MAINTAINERS --- v2.0.33/linux/MAINTAINERS Fri Sep 5 20:43:58 1997 +++ linux/MAINTAINERS Wed Jun 3 15:17:46 1998 @@ -82,7 +82,7 @@ 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] P: Paul Gortmaker -M gpg109@rsphy1.anu.edu.au +M: gpg109@rsphy1.anu.edu.au L: linux-net@vger.rutgers.edu S: Maintained @@ -98,6 +98,12 @@ L: linux-net@vger.rutgers.edu S: Maintained +TLAN NETWORK DRIVER +P: James Banks +M: james.banks@caldera.com +L: linux-net@vger.rutgers.edu +S: Supported + DIGI RIGHTSWITCH NETWORK DRIVER P: Rick Richardson M: rick@dgii.com @@ -342,7 +348,7 @@ RISCOM8 DRIVER: P: Dmitry Gorodchanin -M: begemot@bgm.rosprint.net +M: pgmdsg@ibi.com L: linux-kernel@vger.rutgers.edu S: Maintained @@ -416,6 +422,12 @@ P: H. Peter Anvin M: hpa@zytor.com L: linux-kernel@vger.rutgers.edu +S: Maintained + +WD7000 SCSI DRIVER +P: Miroslav Zagorac +M: zaga@fly.cc.fer.hr +L: linux-scsi@vger.rutgers.edu S: Maintained REST: diff -u --recursive --new-file v2.0.33/linux/Makefile linux/Makefile --- v2.0.33/linux/Makefile Tue Mar 10 13:19:08 1998 +++ linux/Makefile Wed Jun 3 15:17:46 1998 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 0 -SUBLEVEL = 33 +SUBLEVEL = 34 ARCH = i386 @@ -201,6 +201,9 @@ $(TOPDIR)/include/linux/version.h: include/linux/version.h $(TOPDIR)/include/linux/compile.h: include/linux/compile.h + +ksymoops: + $(MAKE) -C scripts ksymoops newversion: @if [ ! -f .version ]; then \ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/Makefile linux/arch/alpha/Makefile --- v2.0.33/linux/arch/alpha/Makefile Mon Aug 5 00:13:50 1996 +++ linux/arch/alpha/Makefile Wed Jun 3 15:17:46 1998 @@ -26,6 +26,21 @@ CFLAGS := $(CFLAGS) -mno-fp-regs +# determine if we can use the BWX instructions with GAS +$(shell rm -f /tmp/GAS_VER) +#$(shell $(AS) --version >& /tmp/GAS_VER) +$(shell $(AS) --version > /tmp/GAS_VER 2>&1) +OLD_GAS := $(shell if cat /tmp/GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi) + +ifneq ($(OLD_GAS),yes) + CFLAGS := $(CFLAGS) -Wa,-m21164a -DBWX_USABLE + +# if PYXIS, then enable use of BWIO space + ifeq ($(CONFIG_ALPHA_PYXIS),y) + CFLAGS := $(CFLAGS) -DBWIO_ENABLED + endif +endif + HEAD := arch/alpha/kernel/head.o SUBDIRS := $(SUBDIRS) arch/alpha/kernel arch/alpha/mm arch/alpha/lib \ @@ -57,3 +72,6 @@ archdep: @$(MAKEBOOT) dep + +bootpfile: + @$(MAKEBOOT) bootpfile diff -u --recursive --new-file v2.0.33/linux/arch/alpha/boot/Makefile linux/arch/alpha/boot/Makefile --- v2.0.33/linux/arch/alpha/boot/Makefile Mon Aug 5 00:13:50 1996 +++ linux/arch/alpha/boot/Makefile Wed Jun 3 15:17:46 1998 @@ -26,6 +26,7 @@ $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< OBJECTS = head.o main.o +BPOBJECTS = head.o bootp.o TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo VMLINUX = $(TOPDIR)/vmlinux OBJSTRIP = tools/objstrip @@ -44,6 +45,9 @@ ( cat tools/lxboot tools/bootlx vmlinux.nh ) > bootimage tools/mkbb bootimage tools/lxboot +bootpfile: tools/bootph vmlinux.nh + ( cat tools/bootph vmlinux.nh ) > bootpfile + srmboot: bootdevice bootimage dd if=bootimage of=$(BOOTDEV) bs=512 seek=1 skip=1 tools/mkbb $(BOOTDEV) tools/lxboot @@ -59,6 +63,8 @@ # main.o: ksize.h +bootp.o: ksize.h + ksize.h: $(OBJSTRIP) vmlinux.nh echo "#define KERNEL_SIZE `$(OBJSTRIP) -p vmlinux.nh /dev/null`" > $@ @@ -82,6 +88,9 @@ tools/bootlx: bootloader $(OBJSTRIP) $(OBJSTRIP) -vb bootloader tools/bootlx +tools/bootph: bootpheader $(OBJSTRIP) + $(OBJSTRIP) -vb bootpheader tools/bootph + $(OBJSTRIP): $(OBJSTRIP).c $(HOSTCC) $(OBJSTRIP).c -o $(OBJSTRIP) @@ -95,8 +104,15 @@ -o bootloader && strip bootloader || \ (rm -f bootloader && exit 1) +bootpheader: $(BPOBJECTS) + $(LD) $(LINKFLAGS) \ + $(BPOBJECTS) \ + $(LIBS) \ + -o bootpheader && strip bootpheader || \ + (rm -f bootpheader && exit 1) + clean: rm -f $(TARGETS) bootloader bootimage vmlinux.nh \ - tools/mkbb tools/bootlx tools/lxboot + tools/mkbb tools/bootlx tools/lxboot ksize.h dep: diff -u --recursive --new-file v2.0.33/linux/arch/alpha/boot/bootp.c linux/arch/alpha/boot/bootp.c --- v2.0.33/linux/arch/alpha/boot/bootp.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/boot/bootp.c Wed Jun 3 15:17:46 1998 @@ -0,0 +1,239 @@ +/* + * arch/alpha/boot/bootp.c + * + * Copyright (C) 1997 Jay Estabrook + * + * This file is used for creating a bootp file for the Linux/AXP kernel + * + * based significantly on the arch/alpha/boot/main.c of Linus Torvalds + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "ksize.h" + +extern int vsprintf(char *, const char *, va_list); +extern unsigned long switch_to_osf_pal(unsigned long nr, + struct pcb_struct * pcb_va, struct pcb_struct * pcb_pa, + unsigned long vptb, unsigned long *kstk); + +int printk(const char * fmt, ...) +{ + va_list args; + int i, j, written, remaining, num_nl; + static char buf[1024]; + char * str; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + + /* expand \n into \r\n: */ + + num_nl = 0; + for (j = 0; j < i; ++j) { + if (buf[j] == '\n') + ++num_nl; + } + remaining = i + num_nl; + for (j = i - 1; j >= 0; --j) { + buf[j + num_nl] = buf[j]; + if (buf[j] == '\n') { + --num_nl; + buf[j + num_nl] = '\r'; + } + } + + str = buf; + do { + written = puts(str, remaining); + remaining -= written; + str += written; + } while (remaining > 0); + return i; +} + +#define hwrpb (*INIT_HWRPB) + +/* + * Find a physical address of a virtual object.. + * + * This is easy using the virtual page table address. + */ +struct pcb_struct * find_pa(unsigned long *vptb, struct pcb_struct * pcb) +{ + unsigned long address = (unsigned long) pcb; + unsigned long result; + + result = vptb[address >> 13]; + result >>= 32; + result <<= 13; + result |= address & 0x1fff; + return (struct pcb_struct *) result; +} + +/* + * This function moves into OSF/1 pal-code, and has a temporary + * PCB for that. The kernel proper should replace this PCB with + * the real one as soon as possible. + * + * The page table muckery in here depends on the fact that the boot + * code has the L1 page table identity-map itself in the second PTE + * in the L1 page table. Thus the L1-page is virtually addressable + * itself (through three levels) at virtual address 0x200802000. + * + * As we don't want it there anyway, we also move the L1 self-map + * up as high as we can, so that the last entry in the L1 page table + * maps the page tables. + * + * As a result, the OSF/1 pal-code will instead use a virtual page table + * map located at 0xffffffe00000000. + */ +#define pcb_va ((struct pcb_struct *) 0x20000000) +#define old_vptb (0x0000000200000000UL) +#define new_vptb (0xfffffffe00000000UL) +void pal_init(void) +{ + unsigned long i, rev, sum; + unsigned long *L1, *l; + struct percpu_struct * percpu; + struct pcb_struct * pcb_pa; + + /* Find the level 1 page table and duplicate it in high memory */ + L1 = (unsigned long *) 0x200802000UL; /* (1<<33 | 1<<23 | 1<<13) */ + L1[1023] = L1[1]; + + percpu = (struct percpu_struct *) + (hwrpb.processor_offset + (unsigned long) &hwrpb), + + pcb_va->ksp = 0; + pcb_va->usp = 0; + pcb_va->ptbr = L1[1] >> 32; + pcb_va->asn = 0; + pcb_va->pcc = 0; + pcb_va->unique = 0; + pcb_va->flags = 1; + pcb_pa = find_pa((unsigned long *) old_vptb, pcb_va); + printk("Switching to OSF PAL-code .. "); + /* + * a0 = 2 (OSF) + * a1 = return address, but we give the asm the vaddr of the PCB + * a2 = physical addr of PCB + * a3 = new virtual page table pointer + * a4 = KSP (but we give it 0, asm sets it) + */ + i = switch_to_osf_pal( + 2, + pcb_va, + pcb_pa, + new_vptb, + 0); + if (i) { + printk("failed, code %ld\n", i); + halt(); + } + rev = percpu->pal_revision = percpu->palcode_avail[2]; + + hwrpb.vptb = new_vptb; + + /* update checksum: */ + sum = 0; + for (l = (unsigned long *) &hwrpb; + l < (unsigned long *) &hwrpb.chksum; + ++l) + sum += *l; + hwrpb.chksum = sum; + + printk("Ok (rev %lx)\n", rev); + /* remove the old virtual page-table mapping */ + L1[1] = 0; + flush_tlb_all(); +} + +static inline long load(unsigned long dst, + unsigned long src, + unsigned long count) +{ + extern void * memcpy(void *, const void *, size_t); + + memcpy((void *)dst, (void *)src, count); + return count; +} + +/* + * Start the kernel. + */ +static void runkernel(void) +{ + __asm__ __volatile__( + "bis %1,%1,$30\n\t" + "bis %0,%0,$26\n\t" + "ret ($26)" + : /* no outputs: it doesn't even return */ + : "r" (START_ADDR), + "r" (PAGE_SIZE + INIT_STACK)); +} + +extern char _end; +#define KERNEL_ORIGIN \ + ((((unsigned long)&_end) + 511) & ~511) + +void start_kernel(void) +{ + static long i; + static int nbytes; + static char envval[256]; + char envbuf[256]; + + printk("Linux/AXP bootp loader for Linux " UTS_RELEASE "\n"); + + if (hwrpb.pagesize != 8192) { + printk("Expected 8kB pages, got %ldkB\n", + hwrpb.pagesize >> 10); + return; + } + pal_init(); + + nbytes = dispatch(CCB_GET_ENV, ENV_BOOTED_OSFLAGS, + envbuf, sizeof(envbuf)); + if (nbytes < 0 || nbytes >= sizeof(envbuf)) { + nbytes = 0; + } + + envbuf[nbytes] = '\0'; + memcpy(envval, envbuf, nbytes+1); + printk("Loading the kernel...'%s'\n", envval); + + /* NOTE: *no* callbacks or printouts from here on out!!! */ + + /* + * HACK alert: + * + * assume direct copy will fail due to overlap of virtual source + * and physical destination, so move it way high physical first, + * then move it back to its final resting place... + * + * this way could fail, too, but it works on the platforms *I* have + */ + i = load(START_ADDR+(4*KERNEL_SIZE), KERNEL_ORIGIN, KERNEL_SIZE); + i = load(START_ADDR, START_ADDR+(4*KERNEL_SIZE), KERNEL_SIZE); + + strcpy((char*)ZERO_PAGE, envval); + + runkernel(); + + for (i = 0 ; i < 0x100000000 ; i++) + /* nothing */; + halt(); +} diff -u --recursive --new-file v2.0.33/linux/arch/alpha/config.in linux/arch/alpha/config.in --- v2.0.33/linux/arch/alpha/config.in Mon Aug 5 00:13:50 1996 +++ linux/arch/alpha/config.in Wed Jun 3 15:17:46 1998 @@ -6,8 +6,12 @@ # clear all implied options (don't want default values for those): unset CONFIG_CROSSCOMPILE CONFIG_NATIVE -unset CONFIG_PCI CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS +unset CONFIG_PCI CONFIG_ALPHA_EISA +unset CONFIG_ALPHA_LCA CONFIG_ALPHA_APECS CONFIG_ALPHA_CIA CONFIG_ALPHA_T2 +unset CONFIG_ALPHA_PYXIS +unset CONFIG_ALPHA_EV4 CONFIG_ALPHA_EV5 unset CONFIG_ALPHA_NEED_ROUNDING_EMULATION +unset CONFIG_ALPHA_SRM CONFIG_ALPHA_SRM_SETUP mainmenu_option next_comment comment 'Code maturity level options' @@ -43,49 +47,123 @@ EB64+ CONFIG_ALPHA_EB64P \ EB164 CONFIG_ALPHA_EB164 \ PC164 CONFIG_ALPHA_PC164 \ + LX164 CONFIG_ALPHA_LX164 \ + SX164 CONFIG_ALPHA_SX164 \ Jensen CONFIG_ALPHA_JENSEN \ Noname CONFIG_ALPHA_NONAME \ + Takara CONFIG_ALPHA_TAKARA \ Mikasa CONFIG_ALPHA_MIKASA \ + Noritake CONFIG_ALPHA_NORITAKE \ Alcor CONFIG_ALPHA_ALCOR \ + Miata CONFIG_ALPHA_MIATA \ + Sable CONFIG_ALPHA_SABLE \ + AlphaBook1 CONFIG_ALPHA_BOOK1 \ + Ruffian CONFIG_ALPHA_RUFFIAN \ Platform2000 CONFIG_ALPHA_P2K" Cabriolet + +if [ "$CONFIG_ALPHA_BOOK1" = "y" ] +then + define_bool CONFIG_ALPHA_NONAME y +fi if [ "$CONFIG_ALPHA_NONAME" = "y" -o "$CONFIG_ALPHA_EB66" = "y" \ -o "$CONFIG_ALPHA_EB66P" = "y" -o "$CONFIG_ALPHA_P2K" = "y" ] then define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_LCA y fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ - -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \ - -o "$CONFIG_ALPHA_XL" = "y" ] + -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_XL" = "y" ] then define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV4 y define_bool CONFIG_ALPHA_APECS y fi if [ "$CONFIG_ALPHA_EB164" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ - -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" ] + -o "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_XLT" = "y" \ + -o "$CONFIG_ALPHA_TAKARA" = "y" ] then define_bool CONFIG_PCI y define_bool CONFIG_ALPHA_EV5 y define_bool CONFIG_ALPHA_CIA y +fi +if [ "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +then + bool 'Pinnacle or Primo CPU daughtercard?' CONFIG_ALPHA_PRIMO + if [ "$CONFIG_ALPHA_PRIMO" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_CIA y +else + define_bool CONFIG_ALPHA_EV4 y + define_bool CONFIG_ALPHA_APECS y + fi + define_bool CONFIG_PCI y +fi +if [ "$CONFIG_ALPHA_SABLE" = "y" ] +then + bool 'EV5 or EV56 CPU daughtercard?' CONFIG_ALPHA_GAMMA + if [ "$CONFIG_ALPHA_GAMMA" = "y" ] + then + define_bool CONFIG_ALPHA_EV5 y else + define_bool CONFIG_ALPHA_EV4 y + fi + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_T2 y +fi +if [ "$CONFIG_ALPHA_MIATA" = "y" -o "$CONFIG_ALPHA_LX164" = "y" \ + -o "$CONFIG_ALPHA_SX164" = "y" -o "$CONFIG_ALPHA_RUFFIAN" = "y" ] +then + define_bool CONFIG_PCI y + define_bool CONFIG_ALPHA_EV5 y + define_bool CONFIG_ALPHA_PYXIS y +fi +if [ "$CONFIG_ALPHA_JENSEN" = "y" ] +then + define_bool CONFIG_ALPHA_EV4 y +fi +if [ "$CONFIG_ALPHA_EV4" = "y" ] +then # EV45 and older do not support all rounding modes in hw: define_bool CONFIG_ALPHA_NEED_ROUNDING_EMULATION y fi if [ "$CONFIG_ALPHA_CABRIOLET" = "y" -o "$CONFIG_ALPHA_AVANTI" = "y" \ -o "$CONFIG_ALPHA_EB64P" = "y" -o "$CONFIG_ALPHA_JENSEN" = "y" \ - -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" ] + -o "$CONFIG_ALPHA_MIKASA" = "y" -o "$CONFIG_ALPHA_ALCOR" = "y" \ + -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_MIATA" = "y" \ + -o "$CONFIG_ALPHA_NORITAKE" = "y" -o "$CONFIG_ALPHA_PC164" = "y" \ + -o "$CONFIG_ALPHA_LX164" = "y" -o "$CONFIG_ALPHA_SX164" = "y" ] then - bool 'Using SRM as bootloader' CONFIG_ALPHA_SRM + bool 'Use SRM as bootloader' CONFIG_ALPHA_SRM + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + if [ "$CONFIG_ALPHA_SRM" = "y" ]; then + bool ' Use SRM PCI setup' CONFIG_ALPHA_SRM_SETUP + fi + fi +fi +if [ "$CONFIG_ALPHA_ALCOR" = "y" -o "$CONFIG_ALPHA_MIKASA" = "y" \ + -o "$CONFIG_ALPHA_SABLE" = "y" -o "$CONFIG_ALPHA_NORITAKE" = "y" ] +then + define_bool CONFIG_ALPHA_EISA y fi if [ "$CONFIG_ALPHA_XL" = "y" ] then define_bool CONFIG_ALPHA_AVANTI y fi +bool 'Kernel GDB support' CONFIG_KGDB +if [ "$CONFIG_KGDB" = "y" ]; then + bool 'Kernel tracing support?' CONFIG_KGDB_TRACING +fi + bool 'Echo console messages on /dev/ttyS0 (COM1)' CONFIG_SERIAL_ECHO if [ "$CONFIG_PCI" = "y" ]; then bool 'TGA Console Support' CONFIG_TGA_CONSOLE + if [ "$CONFIG_TGA_CONSOLE" = "y" ]; then + bool 'VGA Console Support' CONFIG_VGA_CONSOLE + fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE fi @@ -94,6 +172,7 @@ bool 'System V IPC' CONFIG_SYSVIPC tristate 'Kernel support for a.out (ECOFF) binaries' CONFIG_BINFMT_AOUT tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86 endmenu source drivers/block/Config.in @@ -163,3 +242,7 @@ int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 fi endmenu + +if [ "$CONFIG_TGA_CONSOLE" = "n" ]; then + define_bool CONFIG_VGA_CONSOLE y +fi diff -u --recursive --new-file v2.0.33/linux/arch/alpha/defconfig linux/arch/alpha/defconfig --- v2.0.33/linux/arch/alpha/defconfig Fri Sep 5 20:43:58 1997 +++ linux/arch/alpha/defconfig Wed Jun 3 15:17:46 1998 @@ -36,12 +36,16 @@ CONFIG_ALPHA_EV5=y CONFIG_ALPHA_CIA=y CONFIG_ALPHA_SRM=y +CONFIG_KGDB=y +CONFIG_KGDB_TRACING=n # CONFIG_SERIAL_ECHO is not set CONFIG_TGA_CONSOLE=y +CONFIG_VGA_CONSOLE=y CONFIG_NET=y CONFIG_SYSVIPC=y CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_EM86=y # # Floppy, IDE, and other block devices diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile --- v2.0.33/linux/arch/alpha/kernel/Makefile Mon Jun 3 20:06:37 1996 +++ linux/arch/alpha/kernel/Makefile Wed Jun 3 15:17:46 1998 @@ -16,7 +16,12 @@ O_TARGET := kernel.o O_OBJS := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ - bios32.o ptrace.o time.o apecs.o lca.o cia.o ksyms.o + bios32.o ptrace.o time.o apecs.o lca.o cia.o t2.o pyxis.o \ + ksyms.o smc.o + +ifdef CONFIG_KGDB +O_OBJS += kgdb.o +endif all: kernel.o head.o diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/apecs.c linux/arch/alpha/kernel/apecs.c --- v2.0.33/linux/arch/alpha/kernel/apecs.c Mon Jul 8 22:08:18 1996 +++ linux/arch/alpha/kernel/apecs.c Wed Jun 3 15:17:46 1998 @@ -20,7 +20,7 @@ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern int alpha_sys_type; + /* * BIOS32-style PCI interface: */ @@ -33,13 +33,16 @@ # define DBG(args) #endif -#define vulp volatile unsigned long * #define vuip volatile unsigned int * static volatile unsigned int apecs_mcheck_expected = 0; static volatile unsigned int apecs_mcheck_taken = 0; -static unsigned long apecs_jd, apecs_jd1, apecs_jd2; +static unsigned int apecs_jd, apecs_jd1, apecs_jd2; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int APECS_DMA_WIN_BASE = APECS_DMA_WIN_BASE_DEFAULT; +unsigned int APECS_DMA_WIN_SIZE = APECS_DMA_WIN_SIZE_DEFAULT; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -142,15 +145,15 @@ DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)APECS_IOC_DCSR); - *((volatile unsigned int *)APECS_IOC_DCSR) = stat0; + stat0 = *((vuip)APECS_IOC_DCSR); + *((vuip)APECS_IOC_DCSR) = stat0; mb(); DBG(("conf_read: APECS DCSR was 0x%x\n", stat0)); /* if Type1 access, must set HAE #2 */ if (type1) { - haxr2 = *((unsigned int *)APECS_IOC_HAXR2); + haxr2 = *((vuip)APECS_IOC_HAXR2); mb(); - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1; + *((vuip)APECS_IOC_HAXR2) = haxr2 | 1; DBG(("conf_read: TYPE1 access\n")); } @@ -159,7 +162,7 @@ apecs_mcheck_taken = 0; mb(); /* access configuration space: */ - value = *((volatile unsigned int *)addr); + value = *((vuip)addr); mb(); mb(); if (apecs_mcheck_taken) { @@ -179,7 +182,7 @@ draina(); /* now look for any errors */ - stat0 = *((unsigned int *)APECS_IOC_DCSR); + stat0 = *((vuip)APECS_IOC_DCSR); DBG(("conf_read: APECS DCSR after read 0x%x\n", stat0)); if (stat0 & 0xffe0U) { /* is any error bit set? */ /* if not NDEV, print status */ @@ -188,7 +191,7 @@ } /* reset error status: */ - *((volatile unsigned long *)APECS_IOC_DCSR) = stat0; + *((vuip)APECS_IOC_DCSR) = stat0; mb(); wrmces(0x7); /* reset machine check */ value = 0xffffffff; @@ -197,7 +200,7 @@ /* if Type1 access, must reset HAE #2 so normal IO space ops work */ if (type1) { - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1; + *((vuip)APECS_IOC_HAXR2) = haxr2 & ~1; mb(); } restore_flags(flags); @@ -224,22 +227,22 @@ cli(); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)APECS_IOC_DCSR); - *((volatile unsigned int *)APECS_IOC_DCSR) = stat0; + stat0 = *((vuip)APECS_IOC_DCSR); + *((vuip)APECS_IOC_DCSR) = stat0; mb(); /* if Type1 access, must set HAE #2 */ if (type1) { - haxr2 = *((unsigned int *)APECS_IOC_HAXR2); + haxr2 = *((vuip)APECS_IOC_HAXR2); mb(); - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 | 1; + *((vuip)APECS_IOC_HAXR2) = haxr2 | 1; } draina(); apecs_mcheck_expected = 1; mb(); /* access configuration space: */ - *((volatile unsigned int *)addr) = value; + *((vuip)addr) = value; mb(); mb(); apecs_mcheck_expected = 0; @@ -254,7 +257,7 @@ draina(); /* now look for any errors */ - stat0 = *((unsigned int *)APECS_IOC_DCSR); + stat0 = *((vuip)APECS_IOC_DCSR); if (stat0 & 0xffe0U) { /* is any error bit set? */ /* if not NDEV, print status */ if (!(stat0 & 0x0800)) { @@ -262,7 +265,7 @@ } /* reset error status: */ - *((volatile unsigned long *)APECS_IOC_DCSR) = stat0; + *((vuip)APECS_IOC_DCSR) = stat0; mb(); wrmces(0x7); /* reset machine check */ } @@ -270,7 +273,7 @@ /* if Type1 access, must reset HAE #2 so normal IO space ops work */ if (type1) { - *((unsigned int *)APECS_IOC_HAXR2) = haxr2 & ~1; + *((vuip)APECS_IOC_HAXR2) = haxr2 & ~1; mb(); } restore_flags(flags); @@ -417,6 +420,38 @@ *(vuip)APECS_IOC_TB2R = 0; #else /* CONFIG_ALPHA_XL */ +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 1 for enabled and mapped to 0 */ + if ((*(vuip)APECS_IOC_PB1R & (1U<<19)) && (*(vuip)APECS_IOC_TB1R == 0)) + { + APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB1R & 0xfff00000U; + APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM1R & 0xfff00000U; + APECS_DMA_WIN_SIZE += 0x00100000U; +#if 0 + printk("apecs_init: using Window 1 settings\n"); + printk("apecs_init: PB1R 0x%x PM1R 0x%x TB1R 0x%x\n", + *(vuip)APECS_IOC_PB1R, + *(vuip)APECS_IOC_PM1R, + *(vuip)APECS_IOC_TB1R); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if ((*(vuip)APECS_IOC_PB2R & (1U<<19)) && (*(vuip)APECS_IOC_TB2R == 0)) + { + APECS_DMA_WIN_BASE = *(vuip)APECS_IOC_PB2R & 0xfff00000U; + APECS_DMA_WIN_SIZE = *(vuip)APECS_IOC_PM2R & 0xfff00000U; + APECS_DMA_WIN_SIZE += 0x00100000U; +#if 0 + printk("apecs_init: using Window 2 settings\n"); + printk("apecs_init: PB2R 0x%x PM2R 0x%x TB2R 0x%x\n", + *(vuip)APECS_IOC_PB2R, + *(vuip)APECS_IOC_PM2R, + *(vuip)APECS_IOC_TB2R); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, window 2 is disabled. In the future, we may @@ -428,9 +463,11 @@ *(vuip)APECS_IOC_PB1R = 1U<<19 | (APECS_DMA_WIN_BASE & 0xfff00000U); *(vuip)APECS_IOC_PM1R = (APECS_DMA_WIN_SIZE - 1) & 0xfff00000U; *(vuip)APECS_IOC_TB1R = 0; + } #endif /* CONFIG_ALPHA_XL */ #ifdef CONFIG_ALPHA_CABRIOLET +#if 0 /* * JAE: HACK!!! for now, hardwire if configured... * davidm: Older miniloader versions don't set the clockfrequency @@ -448,10 +485,13 @@ unsigned long *l, sum; sum = 0; - for (l = (unsigned long *) hwrpb; l < (unsigned long *) &hwrpb->chksum; ++l) + for (l = (unsigned long *) hwrpb; + l < (unsigned long *) &hwrpb->chksum; + ++l) sum += *l; hwrpb->chksum = sum; } +#endif #endif /* CONFIG_ALPHA_CABRIOLET */ /* @@ -462,10 +502,10 @@ */ { #if 0 - unsigned int haxr2 = *((unsigned int *)APECS_IOC_HAXR2); mb(); + unsigned int haxr2 = *((vuip)APECS_IOC_HAXR2); mb(); if (haxr2) printk("apecs_init: HAXR2 was 0x%x\n", haxr2); #endif - *((unsigned int *)APECS_IOC_HAXR2) = 0; mb(); + *((vuip)APECS_IOC_HAXR2) = 0; mb(); } @@ -474,15 +514,15 @@ int apecs_pci_clr_err(void) { - apecs_jd = *((unsigned long *)APECS_IOC_DCSR); + apecs_jd = *((vuip)APECS_IOC_DCSR); if (apecs_jd & 0xffe0L) { - apecs_jd1 = *((unsigned long *)APECS_IOC_SEAR); - *((unsigned long *)APECS_IOC_DCSR) = apecs_jd | 0xffe1L; - apecs_jd = *((unsigned long *)APECS_IOC_DCSR); + apecs_jd1 = *((vuip)APECS_IOC_SEAR); + *((vuip)APECS_IOC_DCSR) = apecs_jd | 0xffe1L; + apecs_jd = *((vuip)APECS_IOC_DCSR); mb(); } - *((unsigned long *)APECS_IOC_TBIA) = APECS_IOC_TBIA; - apecs_jd2 = *((unsigned long *)APECS_IOC_TBIA); + *((vuip)APECS_IOC_TBIA) = APECS_IOC_TBIA; + apecs_jd2 = *((vuip)APECS_IOC_TBIA); mb(); return 0; } @@ -526,8 +566,8 @@ #define MCHK_NO_DEVSEL 0x205L #define MCHK_NO_TABT 0x204L if (apecs_mcheck_expected && - (((unsigned int)mchk_procdata->paltemp[0] == MCHK_NO_DEVSEL) || - ((unsigned int)mchk_procdata->paltemp[0] == MCHK_NO_TABT)) + (((unsigned int)mchk_header->code == MCHK_NO_DEVSEL) || + ((unsigned int)mchk_header->code == MCHK_NO_TABT)) ) { #else @@ -550,19 +590,21 @@ printk("apecs_machine_check: HW correctable (0x%lx)\n", vector); } else { - printk("APECS machine check:\n"); - printk(" vector=0x%lx la_ptr=0x%lx\n", + printk(KERN_CRIT "APECS machine check:\n"); + printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr); - printk(" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + printk(KERN_CRIT + " pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset); - printk(" expected %d DCSR 0x%lx PEAR 0x%lx\n", + printk(KERN_CRIT " expected %d DCSR 0x%lx PEAR 0x%lx\n", apecs_mcheck_expected, mchk_sysdata->epic_dcsr, mchk_sysdata->epic_pear); ptr = (unsigned long *)la_ptr; for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + printk(KERN_CRIT " +%lx %lx %lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); } #if 0 /* doesn't work with MILO */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c --- v2.0.33/linux/arch/alpha/kernel/bios32.c Sun Feb 2 04:59:20 1997 +++ linux/arch/alpha/kernel/bios32.c Wed Jun 3 15:17:46 1998 @@ -57,7 +57,7 @@ #include #include #include - +#include #define KB 1024 #define MB (1024*KB) @@ -73,6 +73,8 @@ /* + * PCI_MODIFY + * * Temporary internal macro. If this 0, then do not write to any of * the PCI registers, merely read them (i.e., use configuration as * determined by SRM). The SRM seem do be doing a less than perfect @@ -89,34 +91,72 @@ * the graphics card---there have been some rumor that the #9 BIOS * incorrectly resets that address to 0...). */ +#ifdef CONFIG_ALPHA_SRM_SETUP +#define PCI_MODIFY 0 +static struct pci_dev *irq_dev_to_reset[16]; +static unsigned char irq_to_reset[16]; +static int irq_reset_count = 0; +static struct pci_dev *io_dev_to_reset[16]; +static unsigned char io_reg_to_reset[16]; +static unsigned int io_to_reset[16]; +static int io_reset_count = 0; +#else /* SRM_SETUP */ #define PCI_MODIFY 1 +#endif /* SRM_SETUP */ extern struct hwrpb_struct *hwrpb; - #if PCI_MODIFY /* NOTE: we can't just blindly use 64K for machines with EISA busses; they may also have PCI-PCI bridges present, and then we'd configure the bridge incorrectly */ -#if 0 -static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */ +/* NOTE also that we may need this stuff for SRM_SETUP also, since certain + SRM consoles screw up and allocate I/O space addresses > 64K behind + PCI-to_PCI bridges, which can't pass addresses larger than 64K... */ +#if defined(CONFIG_ALPHA_EISA) +static unsigned int io_base = 0x9000; /* start above 8th slot */ #else -static unsigned int io_base = 0xb000; +static unsigned int io_base = 0x8000; #endif #if defined(CONFIG_ALPHA_XL) /* - an AVANTI *might* be an XL, and an XL has only 27 bits of ISA address - that get passed through the PCI<->ISA bridge chip. Because this causes - us to set the PCI->Mem window bases lower than normal, we've gotta allocate - PCI bus devices' memory addresses *above* the PCI<->memory mapping windows, - so that CPU memory DMA addresses issued by a bus device don't conflict - with bus memory addresses, like frame buffer memory for graphics cards. + An XL is AVANTI (APECS) family, *but* it has only 27 bits of ISA address + that get passed through the PCI<->ISA bridge chip. Although this causes + us to set the PCI->Mem window bases lower than normal, we still allocate + PCI bus devices' memory addresses *below* the low DMA mapping window, + and hope they fit below 64Mb (to avoid conflicts), and so that they can + be accessed via SPARSE space. + + We accept the risk that a broken Myrinet card will be put into a true XL + and thus can more easily run into the problem described below. +*/ +static unsigned int mem_base = 16*MB + 2*MB; /* 16M to 64M-1 is avail */ + +#elif defined(CONFIG_ALPHA_LCA) || defined(CONFIG_ALPHA_APECS) +/* + We try to make this address *always* have more than 1 bit set. + this is so that devices like the broken Myrinet card will always have + a PCI memory address that will never match a IDSEL address in + PCI Config space, which can cause problems with early rev cards. + + However, APECS and LCA have only 34 bits for physical addresses, thus + limiting PCI bus memory addresses for SPARSE access to be less than 128Mb. +*/ +static unsigned int mem_base = 64*MB + 2*MB; + +#else +/* + We try to make this address *always* have more than 1 bit set. + this is so that devices like the broken Myrinet card will always have + a PCI memory address that will never match a IDSEL address in + PCI Config space, which can cause problems with early rev cards. + + Because CIA and PYXIS and T2 have more bits for physical addresses, + they support an expanded range of SPARSE memory addresses. */ -static unsigned int mem_base = 1024*MB; -#else /* CONFIG_ALPHA_XL */ -static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */ +static unsigned int mem_base = 128*MB + 16*MB; #endif /* CONFIG_ALPHA_XL */ /* @@ -128,7 +168,7 @@ struct pci_bus *bus; unsigned short cmd; -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) +#if defined(CONFIG_ALPHA_EISA) /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( @@ -139,6 +179,18 @@ } #endif + /* + * we don't have code that will init the CYPRESS bridge correctly + * so we do the next best thing, and depend on the previous + * console code to do the right thing, and ignore it here... :-\ + */ + if ((dev->vendor == 0x1080) && (dev->device == 0xC693)) { +#if 0 + printk("disable_dev: ignoring CYPRESS bridge...\n"); +#endif + return; + } + bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); @@ -160,7 +212,7 @@ unsigned int base, mask, size, reg; unsigned int alignto; -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) +#if defined(CONFIG_ALPHA_EISA) /* * HACK: the PCI-to-EISA bridge does not seem to identify * itself as a bridge... :-( @@ -171,10 +223,36 @@ } #endif + /* + * we don't have code that will init the CYPRESS bridge correctly + * so we do the next best thing, and depend on the previous + * console code to do the right thing, and ignore it here... :-\ + */ + if ((dev->vendor == 0x1080) && (dev->device == 0xC693)) { +#if 0 + printk("layout_dev: ignoring CYPRESS bridge...\n"); +#endif + return; + } + bus = dev->bus; pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + +#ifdef NOT_NOW + if ((dev->vendor == 0x1080) && (dev->device == 0xC693) && + ((PCI_FUNC(dev->devfn) == 1) || (PCI_FUNC(dev->devfn) == 2))) + { /* if primary or secondary IDE on the CYPRESS bridge */ + if ((reg == PCI_BASE_ADDRESS_0) || (reg == PCI_BASE_ADDRESS_1)) { +#if 0 + printk("layout_dev: ignoring CYPRESS IDE base reg 0/1\n"); +#endif + continue; /* ignore first two registers */ + } + } +#endif /* NOT_NOW */ + /* * Figure out how much space and of what type this * device wants. @@ -182,6 +260,10 @@ pcibios_write_config_dword(bus->number, dev->devfn, reg, 0xffffffff); pcibios_read_config_dword(bus->number, dev->devfn, reg, &base); +#if 0 +printk("layout_dev: slot %d fn %d off 0x%x base 0x%x\n", + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), reg, base); +#endif if (!base) { /* this base-address register is unused */ continue; @@ -201,11 +283,23 @@ mask = (~base << 1) | 0x1; size = (mask & base) & 0xffffffff; /* align to multiple of size of minimum base */ +#if 0 alignto = MAX(0x400, size) ; +#else + /* + If we align to 0x800 bounds, we probably avoid + having devices in any 0xzCzz range, which is + where the DE4X5 driver probes for EISA cards. + Adaptecs, especially, resent such intrusions. :-( + */ + alignto = MAX(0x800, size) ; +#endif base = ALIGN(io_base, alignto ); io_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base | 0x1); + DBG_DEVS(("layout_dev: dev 0x%x IO @ 0x%x (0x%x)\n", + dev->device, base, size)); } else { unsigned int type; /* @@ -266,7 +360,7 @@ base = ALIGN(mem_base, alignto); if (size > 7 * 16*MB) { printk("bios32 WARNING: slot %d, function %d " - "requests %dB of contiguous address " + "requests 0x%x bytes of contig address " " space---don't use sparse memory " " accesses on this device!!\n", PCI_SLOT(dev->devfn), @@ -277,7 +371,8 @@ base += 16*MB; base = ALIGN(base, alignto); } - if (base / (128*MB) != (base + size) / (128*MB)) { + if (base / (128*MB) != (base + size) / (128*MB)) + { base &= ~(128*MB - 1); base += (128 + 16)*MB; base = ALIGN(base, alignto); @@ -286,13 +381,15 @@ mem_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base); + DBG_DEVS(("layout_dev: dev 0x%x MEM @ 0x%x (0x%x)\n", + dev->device, base, size)); } - } + } /* end for-loop */ /* enable device: */ if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED || dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_XGA) + dev->class >> 8 == PCI_CLASS_STORAGE_IDE || + dev->class >> 16 == PCI_BASE_CLASS_DISPLAY) { /* * All of these (may) have I/O scattered all around @@ -305,21 +402,24 @@ pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); - DBG_DEVS(("layout_dev: bus %d slot 0x%x VID 0x%x DID 0x%x class 0x%x\n", - bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class)); + + DBG_DEVS(("layout_dev: bus %d slot %d VID 0x%x DID 0x%x class 0x%x cmd 0x%x\n", + bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, + dev->class, cmd|PCI_COMMAND_MASTER)); } -static void layout_bus(struct pci_bus *bus) +static int layout_bus(struct pci_bus *bus) { unsigned int l, tio, bio, tmem, bmem; struct pci_bus *child; struct pci_dev *dev; + int found_vga = 0; DBG_DEVS(("layout_bus: starting bus %d\n", bus->number)); if (!bus->devices && !bus->children) - return; + return 0; /* * Align the current bases on appropriate boundaries (4K for @@ -338,7 +438,11 @@ * decoders are programmed consistently. */ for (dev = bus->devices; dev; dev = dev->sibling) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) +#ifdef CONFIG_ALPHA_BOOK1 + || (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) +#endif /* CONFIG_ALPHA_BOOK1 */ + ) { disable_dev(dev) ; } } @@ -349,9 +453,15 @@ DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number)); for (dev = bus->devices; dev; dev = dev->sibling) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) +#ifdef CONFIG_ALPHA_BOOK1 + || (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) +#endif /* CONFIG_ALPHA_BOOK1 */ + ) { layout_dev(dev); } + if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + found_vga = 1; } /* * Recursively allocate space for all of the sub-buses: @@ -359,7 +469,7 @@ DBG_DEVS(("layout_bus: starting bus %d children\n", bus->number)); for (child = bus->children; child; child = child->next) { - layout_bus(child); + found_vga += layout_bus(child); } /* * Align the current bases on 4K and 1MB boundaries: @@ -375,7 +485,8 @@ */ pcibios_read_config_dword(bridge->bus->number, bridge->devfn, 0x1c, &l); - l = (l & 0xffff0000) | ((bio >> 8) & 0x00f0) | ((tio - 1) & 0xf000); + l = (l & 0xffff0000) | ((bio >> 8) & 0x00f0) | + ((tio - 1) & 0xf000); pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x1c, l); /* @@ -391,10 +502,13 @@ pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x24, 0x0000ffff); /* - * Tell bridge that there is an ISA bus in the system: + * Tell bridge that there is an ISA bus in the system, + * and (possibly) a VGA as well. */ + l = 0x00040000; /* ISA present */ + if (found_vga) l |= 0x00080000; /* VGA present */ pcibios_write_config_dword(bridge->bus->number, bridge->devfn, - 0x3c, 0x00040000); + 0x3c, l); /* * Clear status bits, enable I/O (for downstream I/O), * turn on master enable (for upstream I/O), turn on @@ -404,6 +518,7 @@ pcibios_write_config_dword(bridge->bus->number, bridge->devfn, 0x4, 0xffff0007); } + return found_vga; } #endif /* !PCI_MODIFY */ @@ -501,9 +616,9 @@ } /* - * A small note about bridges and interrupts. The DECchip 21050 (and later chips) - * adheres to the PCI-PCI bridge specification. This says that the interrupts on - * the other side of a bridge are swizzled in the following manner: + * A small note about bridges and interrupts. The DC 21050 (and later chips) + * adheres to the PCI-PCI bridge specification. This says that the interrupts + * on the other side of a bridge are swizzled in the following manner: * * Dev Interrupt Interrupt * Pin on Pin on @@ -541,13 +656,86 @@ return (((pin-1) + slot) % 4) + 1 ; } +#ifdef CONFIG_ALPHA_SRM_SETUP +/* look for mis-configured devices' IO space addresses behind bridges */ +static void check_behind_io(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + unsigned int reg, orig_base, new_base, found_one = 0; + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + /* read the current setting, check for I/O space and >= 64K */ + pcibios_read_config_dword(bus->number, dev->devfn, reg, &orig_base); + if (!orig_base || !(orig_base & PCI_BASE_ADDRESS_SPACE_IO)) + continue; /* unused or non-IO */ + if (orig_base < 64*1024) { +#if 1 +printk("check_behind_io: ALREADY OK! bus %d slot %d base 0x%x\n", + bus->number, PCI_SLOT(dev->devfn), orig_base); +#endif + if (orig_base & ~1) + continue; /* OK! */ + orig_base = 0x12001; /* HACK! FIXME!! */ + } + + /* HACK ALERT! for now, just subtract 32K from the + original address, which should give us addresses + in the range 0x8000 and up */ + new_base = orig_base - 0x8000; +#if 1 +printk("check_behind_io: ALERT! bus %d slot %d old 0x%x new 0x%x\n", + bus->number, PCI_SLOT(dev->devfn), orig_base, new_base); +#endif + pcibios_write_config_dword(bus->number, dev->devfn, + reg, new_base); + + io_dev_to_reset[io_reset_count] = dev; + io_reg_to_reset[io_reset_count] = reg; + io_to_reset[io_reset_count] = orig_base; + io_reset_count++; + found_one++; + } /* end for-loop */ + + /* if any were modified, gotta hack the bridge IO limits too... */ + if (found_one) { + if (bus->self) { + struct pci_dev *bridge = bus->self; + unsigned int l; + /* + * Set up the top and bottom of the PCI I/O segment + * for this bus. + */ + pcibios_read_config_dword(bridge->bus->number, + bridge->devfn, 0x1c, &l); +#if 1 +printk("check_behind_io: ALERT! bus %d slot %d oldLIM 0x%x\n", + bus->number, PCI_SLOT(bridge->devfn), l); +#endif + l = (l & 0xffff0000U) | 0xf080U; /* give it ALL */ + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, 0x1c, l); + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, + 0x3c, 0x00040000); + pcibios_write_config_dword(bridge->bus->number, + bridge->devfn, + 0x4, 0xffff0007); + } else + printk("check_behind_io: WARNING! bus->self NULL\n"); + } +} +#endif /* CONFIG_ALPHA_SRM_SETUP */ + /* * Most evaluation boards share most of the fixup code, which is isolated here. * This function is declared "inline" as only one platform will ever be selected * in any given kernel. If that platform doesn't need this code, we don't want * it around as dead code. */ -static inline void common_fixup(long min_idsel, long max_idsel, long irqs_per_slot, +static inline void common_fixup( + long min_idsel, + long max_idsel, + long irqs_per_slot, char irq_tab[max_idsel - min_idsel + 1][irqs_per_slot], long ide_base) { @@ -559,37 +747,122 @@ * Go through all devices, fixing up irqs as we see fit: */ for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE -#if defined(CONFIG_ALPHA_MIKASA) || defined(CONFIG_ALPHA_ALCOR) + if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) +#if defined(CONFIG_ALPHA_EISA) /* PCEB (PCI to EISA bridge) does not identify itself as a bridge... :-( */ && !((dev->vendor==0x8086) && (dev->device==0x482)) #endif +#ifdef CONFIG_ALPHA_BOOK1 + || (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA) +#endif /* CONFIG_ALPHA_BOOK1 */ ) { dev->irq = 0; /* - * This device is not on the primary bus, we need to figure out which - * interrupt pin it will come in on. We know which slot it will come - * in on 'cos that slot is where the bridge is. Each time the interrupt - * line passes through a PCI-PCI bridge we must apply the swizzle function + * This device is not on the primary bus, + * we need to figure out which + * interrupt pin it will come in on. + * We know which slot it will come + * in on 'cuz that slot is where the bridge is. + * Each time the interrupt + * line passes through a PCI-PCI bridge + * we must apply the swizzle function * (see the inline static routine above). */ if (dev->bus->number != 0) { - struct pci_dev *curr = dev ; - /* read the pin and do the PCI-PCI bridge interrupt pin swizzle */ - pcibios_read_config_byte(dev->bus->number, dev->devfn, - PCI_INTERRUPT_PIN, &pin); + /* read the pin and do the PCI-PCI bridge + interrupt pin swizzle */ + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_PIN, + &pin); /* cope with 0 */ if (pin == 0) pin = 1 ; - /* follow the chain of bridges, swizzling as we go */ + /* follow the chain of bridges, + swizzling as we go */ +#if defined(CONFIG_ALPHA_MIATA) + /* check first for the built-in bridge */ + if ((PCI_SLOT(dev->bus->self->devfn) == 8) || + (PCI_SLOT(dev->bus->self->devfn) == 20)) { + slot = PCI_SLOT(dev->devfn) + 9; +#if 0 + printk("MIATA: bus 1 slot %d pin %d irq %d " + "min_idsel %d\n", + PCI_SLOT(dev->devfn), pin, + irq_tab[slot - min_idsel][pin], + min_idsel); +#endif + } + else /* must be a card-based bridge */ + { + struct pci_dev *curr = dev ; do { + if ((PCI_SLOT(curr->bus->self->devfn) == 8) || + (PCI_SLOT(curr->bus->self->devfn) == 20)) + { + slot = PCI_SLOT(curr->devfn) + 5; + break; + } /* swizzle */ - pin = bridge_swizzle(pin, PCI_SLOT(curr->devfn)) ; + pin = bridge_swizzle( + pin, PCI_SLOT(curr->devfn)) ; + /* move up the chain of bridges */ + curr = curr->bus->self ; + /* slot of the next bridge. */ + slot = PCI_SLOT(curr->devfn); + } while (curr->bus->self) ; + } +#elif defined(CONFIG_ALPHA_NORITAKE) + /* check first for the built-in bridge */ + if (PCI_SLOT(dev->bus->self->devfn) == 8) { + slot = PCI_SLOT(dev->devfn) + 15; /* WAG! */ +#if 0 + printk("NORITAKE: bus 1 slot %d pin %d " + "irq %d min_idsel %d\n", + PCI_SLOT(dev->devfn), pin, + irq_tab[slot - min_idsel][pin], + min_idsel); +#endif + } + else /* must be a card-based bridge */ + { + struct pci_dev *curr = dev ; + do { + if (PCI_SLOT(curr->bus->self->devfn) == 8) { + slot = PCI_SLOT(curr->devfn) + 15; + break; + } + /* swizzle */ + pin = bridge_swizzle( + pin, PCI_SLOT(curr->devfn)) ; + /* move up the chain of bridges */ + curr = curr->bus->self ; + /* slot of the next bridge. */ + slot = PCI_SLOT(curr->devfn); + } while (curr->bus->self) ; + } +#else /* not MIATA or NORITAKE */ + { + struct pci_dev *curr = dev ; + do { + /* swizzle */ + pin = bridge_swizzle( + pin, PCI_SLOT(curr->devfn)) ; /* move up the chain of bridges */ curr = curr->bus->self ; } while (curr->bus->self) ; /* The slot is the slot of the last bridge. */ slot = PCI_SLOT(curr->devfn) ; + } +#endif /* not MIATA or NORITAKE */ +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + must make sure that SRM didn't screw up + and allocate an address > 64K for I/O + space behind a PCI-PCI bridge + */ + check_behind_io(dev); +#endif /* CONFIG_ALPHA_SRM_SETUP */ } else { /* work out the slot */ slot = PCI_SLOT(dev->devfn) ; @@ -601,28 +874,193 @@ } if (irq_tab[slot - min_idsel][pin] != -1) dev->irq = irq_tab[slot - min_idsel][pin]; -#if PCI_MODIFY - /* tell the device: */ + +#ifdef CONFIG_ALPHA_SRM + { + unsigned char irq_orig; + /* read the original SRM-set IRQ and tell */ + pcibios_read_config_byte(dev->bus->number, + dev->devfn, + PCI_INTERRUPT_LINE, + &irq_orig); + if (irq_orig != dev->irq) { + printk("common_fixup: bus %d slot 0x%x " + "SRM IRQ 0x%x changed to 0x%x\n", + dev->bus->number,PCI_SLOT(dev->devfn), + irq_orig, dev->irq); +#ifdef CONFIG_ALPHA_SRM_SETUP + irq_dev_to_reset[irq_reset_count] = dev; + irq_to_reset[irq_reset_count] = irq_orig; + irq_reset_count++; +#endif /* CONFIG_ALPHA_SRM_SETUP */ + } + } +#endif /* SRM */ + + /* always tell the device, so the driver knows */ pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); -#endif + + DBG_DEVS(("common_fixup: bus %d slot 0x%x VID 0x%x DID 0x%x\n" + " int_slot 0x%x pin 0x%x irq 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, + dev->device, slot, pin, dev->irq)); + /* * if it's a VGA, enable its BIOS ROM at C0000 */ if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { - pcibios_write_config_dword(dev->bus->number, + /* but if its a Cirrus 543x/544x DISABLE it, */ + /* since enabling ROM disables the memory... */ + if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) && + (dev->device >= 0x00a0) && + (dev->device <= 0x00ac)) { + pcibios_write_config_dword( + dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x00000000); + } else { + pcibios_write_config_dword( + dev->bus->number, dev->devfn, PCI_ROM_ADDRESS, 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); } } + /* + * if it's a SCSI, disable its BIOS ROM + */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) { + pcibios_write_config_dword(dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x0000000); + } +#ifdef NOT_NOW + if ((dev->vendor == 0x1080) && (dev->device == 0xC693) + && (PCI_FUNC(dev->devfn) == 1)) + { +#if 0 +{ +int i; unsigned char b; unsigned short w; unsigned int d; + pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w); + printk("common_fixup: CYPRESS fn 1: PCI CMD reg = 0x%x\n", w); + pcibios_read_config_word(dev->bus->number, dev->devfn, 6, &w); + printk("common_fixup: CYPRESS fn 1: PCI STS reg = 0x%x\n", w); + for (i = 0x10; i <= 0x14; i+=4) { + pcibios_read_config_dword(dev->bus->number, dev->devfn, i, &d); + printk("common_fixup: CYPRESS fn 1: PCI register offset 0x%x = 0x%x\n",i,d); + } + pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x20, &d); + printk("common_fixup: CYPRESS fn 1: PCI register offset 0x20 = 0x%x\n", d); + for (i = 0x3c; i <= 0x3d; i++) { + pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &b); + printk("common_fixup: CYPRESS fn 1: PCI register offset 0x%x = 0x%x\n",i,b); + } + pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x40, &d); + printk("common_fixup: CYPRESS fn 1: PCI register offset 0x40 = 0x%x\n",d); +} +#endif + } + if ((dev->vendor == 0x1080) && (dev->device == 0xC693) + && (PCI_FUNC(dev->devfn) == 2)) + { +#if 0 +{ +int i; unsigned char b; unsigned short w; unsigned int d; + pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w); + printk("common_fixup: CYPRESS fn 2: PCI CMD reg = 0x%x\n", w); + pcibios_read_config_word(dev->bus->number, dev->devfn, 6, &w); + printk("common_fixup: CYPRESS fn 2: PCI STS reg = 0x%x\n", w); + for (i = 0x10; i <= 0x14; i+=4) { + pcibios_read_config_dword(dev->bus->number, dev->devfn, i, &d); + printk("common_fixup: CYPRESS fn 2: PCI register offset 0x%x = 0x%x\n",i,d); + } + pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x20, &d); + printk("common_fixup: CYPRESS fn 2: PCI register offset 0x20 = 0x%x\n", d); + for (i = 0x3c; i <= 0x3d; i++) { + pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &b); + printk("common_fixup: CYPRESS fn 2: PCI register offset 0x%x = 0x%x\n",i,b); + } + pcibios_read_config_dword(dev->bus->number, dev->devfn, 0x40, &d); + printk("common_fixup: CYPRESS fn 2: PCI register offset 0x40 = 0x%x\n",d); +} +#endif + } +#endif /* NOT_NOW */ + } else { /* it *is* a bridge... */ +#ifdef NOT_NOW + /* + * if it's CYPRESS PCI-ISA bridge, disable IDE + * interrupt routing through PCI (ie do through PIC) + */ + if ((dev->vendor == 0x1080) && (dev->device == 0xC693) + && (PCI_FUNC(dev->devfn) == 0)) + { +#if 0 +{ +int i; unsigned char d; unsigned short w; + pcibios_read_config_word(dev->bus->number, dev->devfn, 4, &w); + printk("common_fixup: CYPRESS fn 0: PCI CMD reg = 0x%x\n", w); + for (i = 0x40; i < 0x50; i++) { + pcibios_read_config_byte(dev->bus->number, dev->devfn, i, &d); + printk("common_fixup: CYPRESS fn 0: PCI register offset 0x%x = 0x%x\n", + i, d); + } + for (i=1; i <= 5; i++) { + outb(i, 0x22); + printk("CY control reg %d: 0x%02x\n", i, inb(0x23)); + } +} +#endif +#if 0 + pcibios_write_config_word(dev->bus->number, + dev->devfn, 0x04, 0x0007); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x40, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x41, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x42, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x43, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x44, 0x27); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x45, 0xe0); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x48, 0xf0); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x49, 0x40); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x4a, 0x00); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x4b, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x4c, 0x80); + pcibios_write_config_byte(dev->bus->number, + dev->devfn, 0x4d, 0x70); +#endif + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); +#if 0 + outb(0, DMA1_CLR_MASK_REG); + outb(0, DMA2_CLR_MASK_REG); +#endif + } +#endif /* NOT_NOW */ + } } if (ide_base) { enable_ide(ide_base); } } + /* * The EB66+ is very similar to the EB66 except that it does not have * the on-board NCR and Tulip chips. In the code below, I have used @@ -639,6 +1077,7 @@ static inline void eb66p_fixup(void) { char irq_tab[5][5] = { + /*INT INTA INTB INTC INTD */ {16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J25 */ {16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J26 */ { -1, -1, -1, -1, -1}, /* IdSel 8, SIO */ @@ -650,7 +1089,7 @@ /* - * The PC164 has 19 PCI interrupts, four from each of the four PCI + * The PC164/LX164 have 19 PCI interrupts, four from each of the four PCI * slots, the SIO, PCI/IDE, and USB. * * Each of the interrupts can be individually masked. This is @@ -693,12 +1132,9 @@ static inline void alphapc164_fixup(void) { - extern int SMCInit(void); + extern void SMC93X_Init(void); char irq_tab[7][5] = { - /* - * int intA intB intC intD - * ---- ---- ---- ---- ---- - */ + /*INT INTA INTB INTC INTD */ { 16+2, 16+2, 16+9, 16+13, 16+17}, /* IdSel 5, slot 2, J20 */ { 16+0, 16+0, 16+7, 16+11, 16+15}, /* IdSel 6, slot 0, J29 */ { 16+1, 16+1, 16+8, 16+12, 16+16}, /* IdSel 7, slot 1, J26 */ @@ -710,7 +1146,7 @@ common_fixup(5, 11, 5, irq_tab, 0); - SMCInit(); + SMC93X_Init(); } /* @@ -729,6 +1165,7 @@ static inline void cabriolet_fixup(void) { char irq_tab[5][5] = { + /*INT INTA INTB INTC INTD */ { 16+2, 16+2, 16+7, 16+11, 16+15}, /* IdSel 5, slot 2, J21 */ { 16+0, 16+0, 16+5, 16+9, 16+13}, /* IdSel 6, slot 0, J19 */ { 16+1, 16+1, 16+6, 16+10, 16+14}, /* IdSel 7, slot 1, J20 */ @@ -785,6 +1222,7 @@ static inline void eb66_and_eb64p_fixup(void) { char irq_tab[5][5] = { + /*INT INTA INTB INTC INTD */ {16+7, 16+7, 16+7, 16+7, 16+7}, /* IdSel 5, slot ?, ?? */ {16+0, 16+0, 16+2, 16+4, 16+9}, /* IdSel 6, slot ?, ?? */ {16+1, 16+1, 16+3, 16+8, 16+10}, /* IdSel 7, slot ?, ?? */ @@ -796,7 +1234,7 @@ /* - * Fixup configuration for MIKASA (NORITAKE is different) + * Fixup configuration for MIKASA * * Summary @ 0x536: * Bit Meaning @@ -848,6 +1286,86 @@ } /* + * Fixup configuration for NORITAKE + * + * Summary @ 0x542, summary register #1: + * Bit Meaning + * 0 All valid ints from summary regs 2 & 3 + * 1 QLOGIC ISP1020A SCSI + * 2 Interrupt Line A from slot 0 + * 3 Interrupt Line B from slot 0 + * 4 Interrupt Line A from slot 1 + * 5 Interrupt line B from slot 1 + * 6 Interrupt Line A from slot 2 + * 7 Interrupt Line B from slot 2 + * 8 Interrupt Line A from slot 3 + * 9 Interrupt Line B from slot 3 + *10 Interrupt Line A from slot 4 + *11 Interrupt Line B from slot 4 + *12 Interrupt Line A from slot 5 + *13 Interrupt Line B from slot 5 + *14 Interrupt Line A from slot 6 + *15 Interrupt Line B from slot 6 + * + * Summary @ 0x544, summary register #2: + * Bit Meaning + * 0 OR of all unmasked ints in SR #2 + * 1 OR of secondary bus ints + * 2 Interrupt Line C from slot 0 + * 3 Interrupt Line D from slot 0 + * 4 Interrupt Line C from slot 1 + * 5 Interrupt line D from slot 1 + * 6 Interrupt Line C from slot 2 + * 7 Interrupt Line D from slot 2 + * 8 Interrupt Line C from slot 3 + * 9 Interrupt Line D from slot 3 + *10 Interrupt Line C from slot 4 + *11 Interrupt Line D from slot 4 + *12 Interrupt Line C from slot 5 + *13 Interrupt Line D from slot 5 + *14 Interrupt Line C from slot 6 + *15 Interrupt Line D from slot 6 + * + * The device to slot mapping looks like: + * + * Slot Device + * 7 Intel PCI-EISA bridge chip + * 8 DEC PCI-PCI bridge chip + * 11 PCI on board slot 0 + * 12 PCI on board slot 1 + * 13 PCI on board slot 2 + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ +static inline void noritake_fixup(void) +{ + char irq_tab[15][5] = { + /*INT INTA INTB INTC INTD */ + /* note: IDSELs 16, 17, and 25 are CORELLE only */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ + { -1, -1, -1, -1, -1}, /* IdSel 17, S3 Trio64 */ + { -1, -1, -1, -1, -1}, /* IdSel 18, PCEB */ + { -1, -1, -1, -1, -1}, /* IdSel 19, PPB */ + { -1, -1, -1, -1, -1}, /* IdSel 20, ???? */ + { -1, -1, -1, -1, -1}, /* IdSel 21, ???? */ + { 16+2, 16+2, 16+3, 32+2, 32+3}, /* IdSel 22, slot 0 */ + { 16+4, 16+4, 16+5, 32+4, 32+5}, /* IdSel 23, slot 1 */ + { 16+6, 16+6, 16+7, 32+6, 32+7}, /* IdSel 24, slot 2 */ + { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 25, slot 3 */ + /* the following 5 are actually on PCI bus 1, across the bridge */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* IdSel 16, QLOGIC */ + { 16+8, 16+8, 16+9, 32+8, 32+9}, /* IdSel 17, slot 3 */ + {16+10, 16+10, 16+11, 32+10, 32+11}, /* IdSel 18, slot 4 */ + {16+12, 16+12, 16+13, 32+12, 32+13}, /* IdSel 19, slot 5 */ + {16+14, 16+14, 16+15, 32+14, 32+15}, /* IdSel 20, slot 6 */ + }; + common_fixup(5, 19, 5, irq_tab, 0); +} + +/* * Fixup configuration for ALCOR * * Summary @ GRU_INT_REQ: @@ -892,8 +1410,10 @@ */ static inline void alcor_fixup(void) { - char irq_tab[6][5] = { + char irq_tab[7][5] = { /*INT INTA INTB INTC INTD */ + /* note: IDSEL 17 is XLT only */ + {16+13, 16+13, 16+13, 16+13, 16+13}, /* IdSel 17, TULIP */ { 16+8, 16+8, 16+9, 16+10, 16+11}, /* IdSel 18, slot 0 */ {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 19, slot 3 */ {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 20, slot 4 */ @@ -901,9 +1421,10 @@ { 16+0, 16+0, 16+1, 16+2, 16+3}, /* IdSel 22, slot 2 */ { 16+4, 16+4, 16+5, 16+6, 16+7}, /* IdSel 23, slot 1 */ }; - common_fixup(7, 12, 5, irq_tab, 0); + common_fixup(6, 12, 5, irq_tab, 0); } +#if 0 /* * Fixup configuration for ALPHA XLT (EV5/EV56) * @@ -959,7 +1480,303 @@ }; common_fixup(6, 12, 5, irq_tab, 0); } +#endif /* 0 */ +/* + * Fixup configuration for ALPHA SABLE (2100) - 2100A is different ?? + * + * Summary Registers (536/53a/53c): + * Bit Meaning + *----------------- + * 0 PCI slot 0 + * 1 NCR810 (builtin) + * 2 TULIP (builtin) + * 3 mouse + * 4 PCI slot 1 + * 5 PCI slot 2 + * 6 keyboard + * 7 floppy + * 8 COM2 + * 9 parallel port + *10 EISA irq 3 + *11 EISA irq 4 + *12 EISA irq 5 + *13 EISA irq 6 + *14 EISA irq 7 + *15 COM1 + *16 EISA irq 9 + *17 EISA irq 10 + *18 EISA irq 11 + *19 EISA irq 12 + *20 EISA irq 13 + *21 EISA irq 14 + *22 NC + *23 IIC + * + * The device to slot mapping looks like: + * + * Slot Device + * 0 TULIP + * 1 SCSI + * 2 PCI-EISA bridge + * 3 none + * 4 none + * 5 none + * 6 PCI on board slot 0 + * 7 PCI on board slot 1 + * 8 PCI on board slot 2 + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ +/* NOTE: the IRQ assignments below are arbitrary, but need to be consistent + with the values in the sable_irq_to_mask[] and sable_mask_to_irq[] tables + in irq.c + */ +static inline void sable_fixup(void) +{ + char irq_tab[9][5] = { + /*INT INTA INTB INTC INTD */ + { 32+0, 32+0, 32+0, 32+0, 32+0}, /* IdSel 0, TULIP */ + { 32+1, 32+1, 32+1, 32+1, 32+1}, /* IdSel 1, SCSI */ + { -1, -1, -1, -1, -1}, /* IdSel 2, SIO */ + { -1, -1, -1, -1, -1}, /* IdSel 3, none */ + { -1, -1, -1, -1, -1}, /* IdSel 4, none */ + { -1, -1, -1, -1, -1}, /* IdSel 5, none */ + { 32+2, 32+2, 32+2, 32+2, 32+2}, /* IdSel 6, slot 0 */ + { 32+3, 32+3, 32+3, 32+3, 32+3}, /* IdSel 7, slot 1 */ + { 32+4, 32+4, 32+4, 32+4, 32+4}, /* IdSel 8, slot 2 */ + }; + common_fixup(0, 8, 5, irq_tab, 0); +} + +/* + * Fixup configuration for MIATA (EV56+PYXIS) + * + * Summary @ PYXIS_INT_REQ: + * Bit Meaning + * 0 Fan Fault + * 1 NMI + * 2 Halt/Reset switch + * 3 none + * 4 CID0 (Riser ID) + * 5 CID1 (Riser ID) + * 6 Interval timer + * 7 PCI-ISA Bridge + * 8 Ethernet + * 9 EIDE (deprecated, ISA 14/15 used) + *10 none + *11 USB + *12 Interrupt Line A from slot 4 + *13 Interrupt Line B from slot 4 + *14 Interrupt Line C from slot 4 + *15 Interrupt Line D from slot 4 + *16 Interrupt Line A from slot 5 + *17 Interrupt line B from slot 5 + *18 Interrupt Line C from slot 5 + *19 Interrupt Line D from slot 5 + *20 Interrupt Line A from slot 1 + *21 Interrupt Line B from slot 1 + *22 Interrupt Line C from slot 1 + *23 Interrupt Line D from slot 1 + *24 Interrupt Line A from slot 2 + *25 Interrupt Line B from slot 2 + *26 Interrupt Line C from slot 2 + *27 Interrupt Line D from slot 2 + *27 Interrupt Line A from slot 3 + *29 Interrupt Line B from slot 3 + *30 Interrupt Line C from slot 3 + *31 Interrupt Line D from slot 3 + * + * The device to slot mapping looks like: + * + * Slot Device + * 3 DC21142 Ethernet + * 4 EIDE CMD646 + * 5 none + * 6 USB + * 7 PCI-ISA bridge + * 8 PCI-PCI Bridge (SBU Riser) + * 9 none + * 10 none + * 11 PCI on board slot 4 (SBU Riser) + * 12 PCI on board slot 5 (SBU Riser) + * + * These are behind the bridge, so I'm not sure what to do... + * + * 13 PCI on board slot 1 (SBU Riser) + * 14 PCI on board slot 2 (SBU Riser) + * 15 PCI on board slot 3 (SBU Riser) + * + * + * This two layered interrupt approach means that we allocate IRQ 16 and + * above for PCI interrupts. The IRQ relates to which bit the interrupt + * comes in on. This makes interrupt processing much easier. + */ +static inline void miata_fixup(void) +{ + extern int es1888_init(void); + extern void SMC669_Init(void); /* might be a MiataGL */ + char irq_tab[18][5] = { + /*INT INTA INTB INTC INTD */ + {16+ 8, 16+ 8, 16+ 8, 16+ 8, 16+ 8}, /* IdSel 14, DC21142/3 */ + { -1, -1, -1, -1, -1}, /* IdSel 15, EIDE */ + { -1, -1, -1, -1, -1}, /* IdSel 16, none */ + { -1, -1, -1, -1, -1}, /* IdSel 17, none */ + { -1, -1, -1, -1, -1}, /* IdSel 18, PCI-ISA */ + { -1, -1, -1, -1, -1}, /* IdSel 19, PCI-PCI old */ + { -1, -1, -1, -1, -1}, /* IdSel 20, none */ + { -1, -1, -1, -1, -1}, /* IdSel 21, none */ + {16+12, 16+12, 16+13, 16+14, 16+15}, /* IdSel 22, slot 4 */ + {16+16, 16+16, 16+17, 16+18, 16+19}, /* IdSel 23, slot 5 */ + /* the following 7 are actually on PCI bus 1, across the bridge */ + {16+11, 16+11, 16+11, 16+11, 16+11}, /* IdSel 24, QLISP on GL */ + { -1, -1, -1, -1, -1}, /* IdSel 25, none */ + { -1, -1, -1, -1, -1}, /* IdSel 26, none */ + { -1, -1, -1, -1, -1}, /* IdSel 27, none */ + {16+20, 16+20, 16+21, 16+22, 16+23}, /* IdSel 28, slot 1 */ + {16+24, 16+24, 16+25, 16+26, 16+27}, /* IdSel 29, slot 2 */ + {16+28, 16+28, 16+29, 16+30, 16+31}, /* IdSel 30, slot 3 */ + /* this bridge is on the main bus of the later original MIATA */ + { -1, -1, -1, -1, -1}, /* IdSel 31, PCI-PCI new */ + }; + common_fixup(3, 20, 5, irq_tab, 0); + SMC669_Init(); /* might be a MiataGL, so try to find one of these */ + es1888_init(); +} + +/* + * Fixup configuration for SX164 (PCA56+PYXIS) + * + * Summary @ PYXIS_INT_REQ: + * Bit Meaning + * 0 RSVD + * 1 NMI + * 2 Halt/Reset switch + * 3 MBZ + * 4 RAZ + * 5 RAZ + * 6 Interval timer (RTC) + * 7 PCI-ISA Bridge + * 8 Interrupt Line A from slot 3 + * 9 Interrupt Line A from slot 2 + *10 Interrupt Line A from slot 1 + *11 Interrupt Line A from slot 0 + *12 Interrupt Line B from slot 3 + *13 Interrupt Line B from slot 2 + *14 Interrupt Line B from slot 1 + *15 Interrupt line B from slot 0 + *16 Interrupt Line C from slot 3 + *17 Interrupt Line C from slot 2 + *18 Interrupt Line C from slot 1 + *19 Interrupt Line C from slot 0 + *20 Interrupt Line D from slot 3 + *21 Interrupt Line D from slot 2 + *22 Interrupt Line D from slot 1 + *23 Interrupt Line D from slot 0 + * + * IdSel + * 5 32 bit PCI option slot 2 + * 6 64 bit PCI option slot 0 + * 7 64 bit PCI option slot 1 + * 8 Cypress I/O + * 9 32 bit PCI option slot 3 + * + */ + +static inline void sx164_fixup(void) +{ + extern void SMC669_Init(void); + char irq_tab[5][5] = { + /*INT INTA INTB INTC INTD */ + { 16+ 9, 16+ 9, 16+13, 16+17, 16+21}, /* IdSel 5 slot 2 J17 */ + { 16+11, 16+11, 16+15, 16+19, 16+23}, /* IdSel 6 slot 0 J19 */ + { 16+10, 16+10, 16+14, 16+18, 16+22}, /* IdSel 7 slot 1 J18 */ + { -1, -1, -1, -1, -1}, /* IdSel 8 SIO */ + { 16+ 8, 16+ 8, 16+12, 16+16, 16+20} /* IdSel 9 slot 3 J15 */ + }; + + common_fixup(5, 9, 5, irq_tab, 0); + + SMC669_Init(); +} + +static inline void ruffian_fixup(void) +{ + struct pci_dev *dev; + + /* + * Go through all devices + */ + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + /* + * if it's a VGA, enable its BIOS ROM at C0000 + */ + if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { + /* but if its a Cirrus 543x/544x DISABLE it, */ + /* since enabling ROM disables the memory... */ + if ((dev->vendor == PCI_VENDOR_ID_CIRRUS) && + (dev->device >= 0x00a0) && + (dev->device <= 0x00ac)) { + pcibios_write_config_dword( + dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x00000000); + } else { + pcibios_write_config_dword( + dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x000c0000 | PCI_ROM_ADDRESS_ENABLE); + } + } + /* + * if it's a SCSI, disable its BIOS ROM + */ + if ((dev->class >> 8) == PCI_CLASS_STORAGE_SCSI) { + pcibios_write_config_dword(dev->bus->number, + dev->devfn, + PCI_ROM_ADDRESS, + 0x0000000); + } + } + } +} + +/* + * The Takara has PCI devices 1, 2, and 3 configured to slots 20, + * 19, and 18 respectively, in the default configuration. They can + * also be jumpered to slots 8, 7, and 6 respectively, which is fun + * because the SIO ISA bridge can also be slot 7. However, the SIO + * doesn't explicitly generate PCI-type interrupts, so we can + * assign it whatever the hell IRQ we like and it doesn't matter. + */ +static inline void takara_fixup(void) +{ + char irq_tab[15][5] = { + { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 6 == device 3 */ + { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 7 == device 2 */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 8 == device 1 */ + { -1, -1, -1, -1, -1}, /* slot 9 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 10 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 11 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 12 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 13 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 14 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 15 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 16 == nothing */ + { -1, -1, -1, -1, -1}, /* slot 17 == nothing */ + { 16+3, 16+3, 16+3, 16+3, 16+3}, /* slot 18 == device 3 */ + { 16+2, 16+2, 16+2, 16+2, 16+2}, /* slot 19 == device 2 */ + { 16+1, 16+1, 16+1, 16+1, 16+1}, /* slot 20 == device 1 */ + }; + + common_fixup(6, 20, 5, irq_tab, 0x26e); +} /* * Fixup configuration for all boards that route the PCI interrupts @@ -1029,7 +1846,12 @@ * they are co-indicated when the platform type "Noname" is * selected... :-( */ +#ifdef CONFIG_ALPHA_BOOK1 + /* for the AlphaBook1, NCR810 SCSI is 14, PCMCIA controller is 15 */ + const unsigned int route_tab = 0x0e0f0a0a; +#else /* CONFIG_ALPHA_BOOK1 */ const unsigned int route_tab = 0x0b0a0f09; +#endif /* CONFIG_ALPHA_BOOK1 */ #else /* CONFIG_ALPHA_NONAME */ const unsigned int route_tab = 0x0b0a090f; #endif /* CONFIG_ALPHA_NONAME */ @@ -1044,7 +1866,11 @@ */ level_bits = 0; for (dev = pci_devices; dev; dev = dev->next) { - if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) + if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) +#ifdef CONFIG_ALPHA_BOOK1 + && (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA) +#endif /* CONFIG_ALPHA_BOOK1 */ + ) continue; dev->irq = 0; if (dev->bus->number != 0) { @@ -1076,16 +1902,17 @@ if (slot < 6 || slot >= 6 + sizeof(pirq_tab)/sizeof(pirq_tab[0])) { printk("bios32.sio_fixup: " - "weird, found device %04x:%04x in non-existent slot %d!!\n", + "weird, found device %04x:%04x " + "in non-existent slot %d!!\n", dev->vendor, dev->device, slot); continue; } pirq = pirq_tab[slot - 6][pin]; DBG_DEVS(("sio_fixup: bus %d slot 0x%x VID 0x%x DID 0x%x\n" - " int_slot 0x%x int_pin 0x%x, pirq 0x%x\n", - dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, - slot, pin, pirq)); + " int_slot 0x%x pin 0x%x pirq 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, + dev->device, slot, pin, pirq)); /* * if it's a VGA, enable its BIOS ROM at C0000 */ @@ -1107,15 +1934,51 @@ dev->irq = (route_tab >> (8 * pirq)) & 0xff; +#ifndef CONFIG_ALPHA_BOOK1 + /* do not set *ANY* level triggers for AlphaBook1 */ /* must set the PCI IRQs to level triggered */ level_bits |= (1 << dev->irq); +#endif /* !CONFIG_ALPHA_BOOK1 */ #if PCI_MODIFY /* tell the device: */ pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq); #endif + +#ifdef CONFIG_ALPHA_BOOK1 + /* + * on the AlphaBook1, the PCMCIA chip (Cirrus 6729) + * is sensitive to PCI bus bursts, so we must DISABLE + * burst mode for the NCR 8xx SCSI... :-( + * + * Note that the NCR810 SCSI driver must preserve the + * setting of the bit in order for this to work. At the + * moment (2.0.29), ncr53c8xx.c does NOT do this, but + * 53c7,8xx.c DOES... + */ + if ((dev->vendor == PCI_VENDOR_ID_NCR) && + ((dev->device == PCI_DEVICE_ID_NCR_53C810) || + (dev->device == PCI_DEVICE_ID_NCR_53C815) || + (dev->device == PCI_DEVICE_ID_NCR_53C820) || + (dev->device == PCI_DEVICE_ID_NCR_53C825) + )) { + unsigned int io_port; + unsigned char ctest4; + + pcibios_read_config_dword(dev->bus->number, dev->devfn, + PCI_BASE_ADDRESS_0, &io_port); + io_port &= PCI_BASE_ADDRESS_IO_MASK; + ctest4 = inb(io_port+0x21); + if (!(ctest4 & 0x80)) { + printk("AlphaBook1 NCR init: setting burst disable\n"); + outb(ctest4 | 0x80, io_port+0x21); + } } +#endif /* CONFIG_ALPHA_BOOK1 */ + + } /* end for devs */ + /* * Now, make all PCI interrupts level sensitive. Notice: * these registers must be accessed byte-wise. inw()/outw() @@ -1125,21 +1988,43 @@ * so that the only bits getting set are for devices actually found. * Note that we do preserve the remainder of the bits, which we hope * will be set correctly by ARC/SRM. + * + * Note: we at least preserve any level-set bits on AlphaBook1 */ level_bits |= ((inb(0x4d0) | (inb(0x4d1) << 8)) & 0x71ff); outb((level_bits >> 0) & 0xff, 0x4d0); outb((level_bits >> 8) & 0xff, 0x4d1); + +#ifdef CONFIG_ALPHA_BOOK1 + { + unsigned char orig, config; + /* on the AlphaBook1, make sure that register PR1 indicates 1Mb mem */ + outb(0x0f, 0x3ce); orig = inb(0x3cf); /* read PR5 */ + outb(0x0f, 0x3ce); outb(0x05, 0x3cf); /* unlock PR0-4 */ + outb(0x0b, 0x3ce); config = inb(0x3cf); /* read PR1 */ + if ((config & 0xc0) != 0xc0) { + printk("AlphaBook1 VGA init: setting 1Mb memory\n"); + config |= 0xc0; + outb(0x0b, 0x3ce); outb(config, 0x3cf); /* write PR1 */ + } + outb(0x0f, 0x3ce); outb(orig, 0x3cf); /* (re)lock PR0-4 */ + } +#endif /* CONFIG_ALPHA_BOOK1 */ + +#ifndef CONFIG_ALPHA_BOOK1 + /* do not do IDE init for AlphaBook1 */ enable_ide(0x26e); +#endif /* !CONFIG_ALPHA_BOOK1 */ } #ifdef CONFIG_TGA_CONSOLE -extern void tga_console_init(void); +extern void tga_console_find(void); #endif /* CONFIG_TGA_CONSOLE */ unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) { -#if PCI_MODIFY +#if PCI_MODIFY && !defined(CONFIG_ALPHA_RUFFIAN) /* * Scan the tree, allocating PCI memory and I/O space. */ @@ -1153,7 +2038,7 @@ sio_fixup(); #elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB164) cabriolet_fixup(); -#elif defined(CONFIG_ALPHA_PC164) +#elif defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) alphapc164_fixup(); #elif defined(CONFIG_ALPHA_EB66P) eb66p_fixup(); @@ -1163,16 +2048,26 @@ eb66_and_eb64p_fixup(); #elif defined(CONFIG_ALPHA_MIKASA) mikasa_fixup(); -#elif defined(CONFIG_ALPHA_ALCOR) +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) alcor_fixup(); -#elif defined(CONFIG_ALPHA_XLT) - xlt_fixup(); +#elif defined(CONFIG_ALPHA_SABLE) + sable_fixup(); +#elif defined(CONFIG_ALPHA_MIATA) + miata_fixup(); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_fixup(); +#elif defined(CONFIG_ALPHA_SX164) + sx164_fixup(); +#elif defined(CONFIG_ALPHA_TAKARA) + takara_fixup(); +#elif defined(CONFIG_ALPHA_RUFFIAN) + ruffian_fixup(); #else # error You must tell me what kind of platform you want. #endif #ifdef CONFIG_TGA_CONSOLE - tga_console_init(); + tga_console_find(); #endif /* CONFIG_TGA_CONSOLE */ return mem_start; @@ -1282,262 +2177,117 @@ } return err; } -#ifdef CONFIG_ALPHA_PC164 - -/* device "activate" register contents */ -#define DEVICE_ON 1 -#define DEVICE_OFF 0 - -/* configuration on/off keys */ -#define CONFIG_ON_KEY 0x55 -#define CONFIG_OFF_KEY 0xaa - -/* configuration space device definitions */ -#define FDC 0 -#define IDE1 1 -#define IDE2 2 -#define PARP 3 -#define SER1 4 -#define SER2 5 -#define RTCL 6 -#define KYBD 7 -#define AUXIO 8 - -/* Chip register offsets from base */ -#define CONFIG_CONTROL 0x02 -#define INDEX_ADDRESS 0x03 -#define LOGICAL_DEVICE_NUMBER 0x07 -#define DEVICE_ID 0x20 -#define DEVICE_REV 0x21 -#define POWER_CONTROL 0x22 -#define POWER_MGMT 0x23 -#define OSC 0x24 - -#define ACTIVATE 0x30 -#define ADDR_HI 0x60 -#define ADDR_LO 0x61 -#define INTERRUPT_SEL 0x70 -#define INTERRUPT_SEL_2 0x72 /* KYBD/MOUS only */ -#define DMA_CHANNEL_SEL 0x74 /* FDC/PARP only */ - -#define FDD_MODE_REGISTER 0x90 -#define FDD_OPTION_REGISTER 0x91 - -/* values that we read back that are expected ... */ -#define VALID_DEVICE_ID 2 - -/* default device addresses */ -#define KYBD_INTERRUPT 1 -#define MOUS_INTERRUPT 12 -#define COM2_BASE 0x2f8 -#define COM2_INTERRUPT 3 -#define COM1_BASE 0x3f8 -#define COM1_INTERRUPT 4 -#define PARP_BASE 0x3bc -#define PARP_INTERRUPT 7 - -#define SMC_DEBUG 0 - -unsigned long SMCConfigState( unsigned long baseAddr ) -{ - unsigned char devId; - unsigned char devRev; - - unsigned long configPort; - unsigned long indexPort; - unsigned long dataPort; - - configPort = indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )configPort + 1 ); - - outb(CONFIG_ON_KEY, configPort); - outb(CONFIG_ON_KEY, configPort); - outb(DEVICE_ID, indexPort); - devId = inb(dataPort); - if ( devId == VALID_DEVICE_ID ) { - outb(DEVICE_REV, indexPort); - devRev = inb(dataPort); - } - else { - baseAddr = 0; - } - return( baseAddr ); -} - -void SMCRunState( unsigned long baseAddr ) -{ - outb(CONFIG_OFF_KEY, baseAddr); -} - -unsigned long SMCDetectUltraIO(void) -{ - unsigned long baseAddr; - - baseAddr = 0x3F0; - if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { - return( baseAddr ); - } - baseAddr = 0x370; - if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { - return( baseAddr ); - } - return( ( unsigned long )0 ); -} - -void SMCEnableDevice( unsigned long baseAddr, - unsigned long device, - unsigned long portaddr, - unsigned long interrupt) -{ - unsigned long indexPort; - unsigned long dataPort; - - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(device, dataPort); - - outb(ADDR_LO, indexPort); - outb(( portaddr & 0xFF ), dataPort); - - outb(ADDR_HI, indexPort); - outb(( ( portaddr >> 8 ) & 0xFF ), dataPort); - - outb(INTERRUPT_SEL, indexPort); - outb(interrupt, dataPort); - - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); -} +#if (defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) || \ + defined(CONFIG_ALPHA_SX164) || \ + defined(CONFIG_ALPHA_EB164) || \ + defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_CABRIOLET)) && defined(CONFIG_ALPHA_SRM) -void SMCEnableKYBD( unsigned long baseAddr ) +/* + on the above machines, under SRM console, we must use the CSERVE PALcode + routine to manage the interrupt mask for us, otherwise, the kernel/HW get + out of sync with what the PALcode thinks it needs to deliver/ignore + */ +void +cserve_update_hw(unsigned long irq, unsigned long mask) { - unsigned long indexPort; - unsigned long dataPort; - - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); - - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(KYBD, dataPort); - - outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ - outb(KYBD_INTERRUPT, dataPort); + extern void cserve_ena(unsigned long); + extern void cserve_dis(unsigned long); - outb(INTERRUPT_SEL_2, indexPort);/* Secondary interrupt select */ - outb(MOUS_INTERRUPT, dataPort); - - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); + if (mask & (1UL << irq)) + /* disable */ + cserve_dis(irq - 16); + else + /* enable */ + cserve_ena(irq - 16); + return; +} +#endif /* (PC164 || LX164 || SX164 || EB164 || CABRIO) && SRM */ + +#ifdef CONFIG_ALPHA_MIATA + +/* init the built-in ES1888 sound chip (SB16 compatible) */ +int es1888_init(void) +{ + /* sequence of IO reads to init the audio controller */ + inb(0x0229); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0229); + inb(0x022b); + inb(0x0229); + inb(0x0220); /* this sets the base address to 0x220 */ + + /* sequence to set DMA channels */ + outb(0x01, 0x0226); /* reset */ + inb(0x0226); /* pause */ + outb(0x00, 0x0226); /* release reset */ + while (!(inb(0x022e) & 0x80)) /* wait for bit 7 to assert*/ + continue; + inb(0x022a); /* pause */ + outb(0xc6, 0x022c); /* enable extended mode */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb1, 0x022c); /* setup for write to Interrupt CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x14, 0x022c); /* set IRQ 5 */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0xb2, 0x022c); /* setup for write to DMA CR */ + while (inb(0x022c) & 0x80) /* wait for bit 7 to deassert */ + continue; + outb(0x18, 0x022c); /* set DMA channel 1 */ + + return 0; } -void SMCEnableFDC( unsigned long baseAddr ) -{ - unsigned long indexPort; - unsigned long dataPort; - - unsigned char oldValue; - - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); +#endif /* CONFIG_ALPHA_MIATA */ - outb(LOGICAL_DEVICE_NUMBER, indexPort); - outb(FDC, dataPort); - - outb(FDD_MODE_REGISTER, indexPort); - oldValue = inb(dataPort); - - oldValue |= 0x0E; /* Enable burst mode */ - outb(oldValue, dataPort); - - outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ - outb(0x06, dataPort ); - - outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ - outb(0x02, dataPort); - - outb(ACTIVATE, indexPort); - outb(DEVICE_ON, dataPort); -} - -#if SMC_DEBUG -void SMCReportDeviceStatus( unsigned long baseAddr ) +#ifdef CONFIG_ALPHA_SRM_SETUP +void reset_for_srm(void) { - unsigned long indexPort; - unsigned long dataPort; - unsigned char currentControl; - - indexPort = baseAddr; - dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); - - outb(POWER_CONTROL, indexPort); - currentControl = inb(dataPort); - - if ( currentControl & ( 1 << FDC ) ) - printk( "\t+FDC Enabled\n" ); - else - printk( "\t-FDC Disabled\n" ); - - if ( currentControl & ( 1 << IDE1 ) ) - printk( "\t+IDE1 Enabled\n" ); - else - printk( "\t-IDE1 Disabled\n" ); - - if ( currentControl & ( 1 << IDE2 ) ) - printk( "\t+IDE2 Enabled\n" ); - else - printk( "\t-IDE2 Disabled\n" ); - - if ( currentControl & ( 1 << PARP ) ) - printk( "\t+PARP Enabled\n" ); - else - printk( "\t-PARP Disabled\n" ); - - if ( currentControl & ( 1 << SER1 ) ) - printk( "\t+SER1 Enabled\n" ); - else - printk( "\t-SER1 Disabled\n" ); - - if ( currentControl & ( 1 << SER2 ) ) - printk( "\t+SER2 Enabled\n" ); - else - printk( "\t-SER2 Disabled\n" ); + extern void scrreset(void); + struct pci_dev *dev; + int i; - printk( "\n" ); -} + /* reset any IRQs that we changed */ + for (i = 0; i < irq_reset_count; i++) { + dev = irq_dev_to_reset[i]; + + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_LINE, irq_to_reset[i]); +#if 1 + printk("reset_for_srm: bus %d slot 0x%x " + "SRM IRQ 0x%x changed back from 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + irq_to_reset[i], dev->irq); #endif + } -int SMCInit(void) -{ - unsigned long SMCUltraBase; - - if ( ( SMCUltraBase = SMCDetectUltraIO( ) ) != ( unsigned long )0 ) { - printk( "SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", - SMCUltraBase ); -#if SMC_DEBUG - SMCReportDeviceStatus( SMCUltraBase ); -#endif - SMCEnableDevice( SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT ); - SMCEnableDevice( SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT ); - SMCEnableDevice( SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT ); - /* on PC164, IDE on the SMC is not enabled; CMD646 (PCI) on MB */ - SMCEnableKYBD( SMCUltraBase ); - SMCEnableFDC( SMCUltraBase ); -#if SMC_DEBUG - SMCReportDeviceStatus( SMCUltraBase ); + /* reset any IO addresses that we changed */ + for (i = 0; i < io_reset_count; i++) { + dev = io_dev_to_reset[i]; + + pcibios_write_config_byte(dev->bus->number, dev->devfn, + io_reg_to_reset[i], io_to_reset[i]); +#if 1 + printk("reset_for_srm: bus %d slot 0x%x " + "SRM IO restored to 0x%x\n", + dev->bus->number, PCI_SLOT(dev->devfn), + io_to_reset[i]); #endif - SMCRunState( SMCUltraBase ); - return( 1 ); - } - else { -#if SMC_DEBUG - printk( "No SMC FDC37C93X Ultra I/O Controller found\n" ); -#endif - return( 0 ); - } } -#endif /* CONFIG_ALPHA_PC164 */ + /* reset the visible screen to the top of display memory */ + scrreset(); +} +#endif /* CONFIG_ALPHA_SRM_SETUP */ #endif /* CONFIG_PCI */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/cia.c linux/arch/alpha/kernel/cia.c --- v2.0.33/linux/arch/alpha/kernel/cia.c Sat Jun 8 01:24:57 1996 +++ linux/arch/alpha/kernel/cia.c Wed Jun 3 15:17:46 1998 @@ -20,26 +20,51 @@ extern struct hwrpb_struct *hwrpb; extern asmlinkage void wrmces(unsigned long mces); -extern int alpha_sys_type; + +/* + * Machine check reasons. Defined according to PALcode sources + * (osf.h and platform.h). + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + /* * BIOS32-style PCI interface: */ #ifdef CONFIG_ALPHA_CIA -#ifdef DEBUG -# define DBG(args) printk args +/* #define DEBUG_MCHECK */ +/* #define DEBUG_CONFIG */ +/* #define DEBUG_DUMP_REGS */ + +#ifdef DEBUG_MCHECK +# define DBGM(args) printk args #else -# define DBG(args) +# define DBGM(args) +#endif +#ifdef DEBUG_CONFIG +# define DBGC(args) printk args +#else +# define DBGC(args) #endif -#define vulp volatile unsigned long * #define vuip volatile unsigned int * static volatile unsigned int CIA_mcheck_expected = 0; static volatile unsigned int CIA_mcheck_taken = 0; -static unsigned long CIA_jd, CIA_jd1, CIA_jd2; +static unsigned int CIA_jd; +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int CIA_DMA_WIN_BASE = CIA_DMA_WIN_BASE_DEFAULT; +unsigned int CIA_DMA_WIN_SIZE = CIA_DMA_WIN_SIZE_DEFAULT; +unsigned long cia_sm_base_r1, cia_sm_base_r2, cia_sm_base_r3; +#endif /* SRM_SETUP */ /* * Given a bus, device, and function number, compute resulting @@ -88,7 +113,7 @@ { unsigned long addr; - DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", + DBGC(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", bus, device_fn, where, pci_addr, type1)); if (bus == 0) { @@ -97,7 +122,8 @@ /* type 0 configuration cycle: */ if (device > 20) { - DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", device)); + DBGC(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); return -1; } @@ -109,7 +135,7 @@ addr = (bus << 16) | (device_fn << 8) | (where); } *pci_addr = addr; - DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + DBGC(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); return 0; } @@ -120,22 +146,25 @@ unsigned int stat0, value; unsigned int cia_cfg = 0; /* to keep gcc quiet */ + value = 0xffffffffU; + mb(); + save_flags(flags); /* avoid getting hit by machine check */ cli(); - DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + DBGC(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR); - *((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0; + stat0 = *((vuip)CIA_IOC_CIA_ERR); + *((vuip)CIA_IOC_CIA_ERR) = stat0; mb(); - DBG(("conf_read: CIA ERR was 0x%x\n", stat0)); + DBGC(("conf_read: CIA ERR was 0x%x\n", stat0)); /* if Type1 access, must set CIA CFG */ if (type1) { - cia_cfg = *((unsigned int *)CIA_IOC_CFG); + cia_cfg = *((vuip)CIA_IOC_CFG); + *((vuip)CIA_IOC_CFG) = cia_cfg | 1; mb(); - *((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1; - DBG(("conf_read: TYPE1 access\n")); + DBGC(("conf_read: TYPE1 access\n")); } mb(); @@ -144,7 +173,7 @@ CIA_mcheck_taken = 0; mb(); /* access configuration space: */ - value = *((volatile unsigned int *)addr); + value = *((vuip)addr); mb(); mb(); if (CIA_mcheck_taken) { @@ -154,39 +183,14 @@ } CIA_mcheck_expected = 0; mb(); - /* - * david.rusling@reo.mts.dec.com. This code is needed for the - * EB64+ as it does not generate a machine check (why I don't - * know). When we build kernels for one particular platform - * then we can make this conditional on the type. - */ -#if 0 - draina(); - - /* now look for any errors */ - stat0 = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("conf_read: CIA ERR after read 0x%x\n", stat0)); - if (stat0 & 0x8280U) { /* is any error bit set? */ - /* if not NDEV, print status */ - if (!(stat0 & 0x0080)) { - printk("CIA.c:conf_read: got stat0=%x\n", stat0); - } - - /* reset error status: */ - *((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0; - mb(); - wrmces(0x7); /* reset machine check */ - value = 0xffffffff; - } -#endif /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1; + *((vuip)CIA_IOC_CFG) = cia_cfg & ~1; mb(); } - DBG(("conf_read(): finished\n")); + DBGC(("conf_read(): finished\n")); restore_flags(flags); return value; @@ -203,60 +207,36 @@ cli(); /* reset status register to avoid losing errors: */ - stat0 = *((volatile unsigned int *)CIA_IOC_CIA_ERR); - *((volatile unsigned int *)CIA_IOC_CIA_ERR) = stat0; + stat0 = *((vuip)CIA_IOC_CIA_ERR); + *((vuip)CIA_IOC_CIA_ERR) = stat0; mb(); - DBG(("conf_write: CIA ERR was 0x%x\n", stat0)); + DBGC(("conf_write: CIA ERR was 0x%x\n", stat0)); /* if Type1 access, must set CIA CFG */ if (type1) { - cia_cfg = *((unsigned int *)CIA_IOC_CFG); + cia_cfg = *((vuip)CIA_IOC_CFG); + *((vuip)CIA_IOC_CFG) = cia_cfg | 1; mb(); - *((unsigned int *)CIA_IOC_CFG) = cia_cfg | 1; - DBG(("conf_read: TYPE1 access\n")); + DBGC(("conf_write: TYPE1 access\n")); } draina(); CIA_mcheck_expected = 1; mb(); /* access configuration space: */ - *((volatile unsigned int *)addr) = value; + *((vuip)addr) = value; mb(); mb(); - CIA_mcheck_expected = 0; - mb(); - /* - * david.rusling@reo.mts.dec.com. This code is needed for the - * EB64+ as it does not generate a machine check (why I don't - * know). When we build kernels for one particular platform - * then we can make this conditional on the type. - */ -#if 0 - draina(); - /* now look for any errors */ - stat0 = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("conf_write: CIA ERR after write 0x%x\n", stat0)); - if (stat0 & 0x8280U) { /* is any error bit set? */ - /* if not NDEV, print status */ - if (!(stat0 & 0x0080)) { - printk("CIA.c:conf_read: got stat0=%x\n", stat0); - } - - /* reset error status: */ - *((volatile unsigned long *)CIA_IOC_CIA_ERR) = stat0; + CIA_mcheck_expected = 0; mb(); - wrmces(0x7); /* reset machine check */ - value = 0xffffffff; - } -#endif /* if Type1 access, must reset IOC CFG so normal IO space ops work */ if (type1) { - *((unsigned int *)CIA_IOC_CFG) = cia_cfg & ~1; + *((vuip)CIA_IOC_CFG) = cia_cfg & ~1; mb(); } - DBG(("conf_write(): finished\n")); + DBGC(("conf_write(): finished\n")); restore_flags(flags); } @@ -377,16 +357,135 @@ unsigned long cia_init(unsigned long mem_start, unsigned long mem_end) { - unsigned int cia_err ; + unsigned int cia_tmp; + +#ifdef DEBUG_DUMP_REGS + { + unsigned int temp; +#if 1 + temp = *((vuip)CIA_IOC_CIA_REV); mb(); + printk("CIA_init: CIA_REV was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_LAT); mb(); + printk("CIA_init: CIA_PCI_LAT was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CIA_CTRL); mb(); + printk("CIA_init: CIA_CTRL was 0x%x\n", temp); + temp = *((vuip)0xfffffc8740000140UL); mb(); + printk("CIA_init: CIA_CTRL1 was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_HAE_MEM); mb(); + printk("CIA_init: CIA_HAE_MEM was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_HAE_IO); mb(); + printk("CIA_init: CIA_HAE_IO was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CFG); mb(); + printk("CIA_init: CIA_CFG was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CACK_EN); mb(); + printk("CIA_init: CIA_CACK_EN was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CFG); mb(); + printk("CIA_init: CIA_CFG was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CIA_DIAG); mb(); + printk("CIA_init: CIA_DIAG was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_DIAG_CHECK); mb(); + printk("CIA_init: CIA_DIAG_CHECK was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PERF_MONITOR); mb(); + printk("CIA_init: CIA_PERF_MONITOR was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PERF_CONTROL); mb(); + printk("CIA_init: CIA_PERF_CONTROL was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CIA_ERR); mb(); + printk("CIA_init: CIA_ERR was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_CIA_STAT); mb(); + printk("CIA_init: CIA_STAT was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_MCR); mb(); + printk("CIA_init: CIA_MCR was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_ERR_MASK); mb(); + printk("CIA_init: CIA_ERR_MASK was 0x%x\n", temp); +#endif + temp = *((vuip)CIA_IOC_PCI_W0_BASE); mb(); + printk("CIA_init: W0_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W1_BASE); mb(); + printk("CIA_init: W1_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W2_BASE); mb(); + printk("CIA_init: W2_BASE was 0x%x\n", temp); + temp = *((vuip)CIA_IOC_PCI_W3_BASE); mb(); + printk("CIA_init: W3_BASE was 0x%x\n", temp); + } +#endif /* DEBUG_DUMP_REGS */ /* * Set up error reporting. */ - cia_err = *(vuip)CIA_IOC_CIA_ERR ; - cia_err |= (0x1 << 7) ; /* master abort */ - *(vuip)CIA_IOC_CIA_ERR = cia_err ; + cia_tmp = *(vuip)CIA_IOC_CIA_ERR; + cia_tmp |= 0x180 ; /* master, target abort */ + *(vuip)CIA_IOC_CIA_ERR = cia_tmp ; mb() ; + cia_tmp = *(vuip)CIA_IOC_CIA_CTRL; + cia_tmp |= 0x400; /* turn on FILL_ERR to get mchecks */ + *(vuip)CIA_IOC_CIA_CTRL = cia_tmp ; + mb() ; + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W0_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T0_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W0_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W0_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 0 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W0_BASE, + *(vuip)CIA_IOC_PCI_W0_MASK, + *(vuip)CIA_IOC_PCI_T0_BASE); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W1_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T1_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W1_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W1_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 1 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W1_BASE, + *(vuip)CIA_IOC_PCI_W1_MASK, + *(vuip)CIA_IOC_PCI_T1_BASE); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W2_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T2_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W2_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W2_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 2 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W2_BASE, + *(vuip)CIA_IOC_PCI_W2_MASK, + *(vuip)CIA_IOC_PCI_T2_BASE); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vuip)CIA_IOC_PCI_W3_BASE & 3) == 1) && + (*(vuip)CIA_IOC_PCI_T3_BASE == 0)) + { + CIA_DMA_WIN_BASE = *(vuip)CIA_IOC_PCI_W3_BASE & 0xfff00000U; + CIA_DMA_WIN_SIZE = *(vuip)CIA_IOC_PCI_W3_MASK & 0xfff00000U; + CIA_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("cia_init: using Window 3 settings\n"); + printk("cia_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)CIA_IOC_PCI_W3_BASE, + *(vuip)CIA_IOC_PCI_W3_MASK, + *(vuip)CIA_IOC_PCI_T3_BASE); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, windows 1,2 and 3 are disabled. In the future, we may @@ -401,6 +500,7 @@ *(vuip)CIA_IOC_PCI_W1_BASE = 0x0 ; *(vuip)CIA_IOC_PCI_W2_BASE = 0x0 ; *(vuip)CIA_IOC_PCI_W3_BASE = 0x0 ; + } /* * check ASN in HWRPB for validity, report if bad @@ -412,17 +512,44 @@ } /* - * Finally, clear the CIA_CFG register, which gets used + * Next, clear the CIA_CFG register, which gets used * for PCI Config Space accesses. That is the way * we want to use it, and we do not want to depend on * what ARC or SRM might have left behind... */ { + unsigned int cia_cfg = *((vuip)CIA_IOC_CFG); mb(); + if (cia_cfg) { + printk("CIA_init: CFG was 0x%x\n", cia_cfg); + *((vuip)CIA_IOC_CFG) = 0; mb(); + } + } + + { + unsigned int cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM); + unsigned int cia_hae_io = *((vuip)CIA_IOC_HAE_IO); #if 0 - unsigned int cia_cfg = *((unsigned int *)CIA_IOC_CFG); mb(); - if (cia_cfg) printk("CIA_init: CFG was 0x%x\n", cia_cfg); + printk("CIA_init: HAE_MEM was 0x%x\n", cia_hae_mem); + printk("CIA_init: HAE_IO was 0x%x\n", cia_hae_io); #endif - *((unsigned int *)CIA_IOC_CFG) = 0; mb(); +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + sigh... For the SRM setup, unless we know apriori what the HAE + contents will be, we need to setup the arbitrary region bases + so we can test against the range of addresses and tailor the + region chosen for the SPARSE memory access. + + see include/asm-alpha/cia.h for the SPARSE mem read/write + */ + cia_sm_base_r1 = (cia_hae_mem ) & 0xe0000000UL; /* region 1 */ + cia_sm_base_r2 = (cia_hae_mem << 16) & 0xf8000000UL; /* region 2 */ + cia_sm_base_r3 = (cia_hae_mem << 24) & 0xfc000000UL; /* region 3 */ +#else /* SRM_SETUP */ + *((vuip)CIA_IOC_HAE_MEM) = 0; mb(); + cia_hae_mem = *((vuip)CIA_IOC_HAE_MEM); + *((vuip)CIA_IOC_HAE_IO) = 0; mb(); + cia_hae_io = *((vuip)CIA_IOC_HAE_IO); +#endif /* SRM_SETUP */ } return mem_start; @@ -430,9 +557,9 @@ int cia_pci_clr_err(void) { - CIA_jd = *((unsigned int *)CIA_IOC_CIA_ERR); - DBG(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); - *((unsigned long *)CIA_IOC_CIA_ERR) = 0x0080; + CIA_jd = *((vuip)CIA_IOC_CIA_ERR); + DBGM(("CIA_pci_clr_err: CIA ERR after read 0x%x\n", CIA_jd)); + *((vuip)CIA_IOC_CIA_ERR) = 0x0180; mb(); return 0; } @@ -440,41 +567,45 @@ void cia_machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) { -#if 0 - printk("CIA machine check ignored\n") ; -#else struct el_common *mchk_header; + struct el_procdata *mchk_procdata; struct el_CIA_sysdata_mcheck *mchk_sysdata; + unsigned long * ptr; + const char * reason; + char buf[128]; + long i; mchk_header = (struct el_common *)la_ptr; - + mchk_procdata = + (struct el_procdata *)(la_ptr + mchk_header->proc_offset); mchk_sysdata = (struct el_CIA_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset); - DBG(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr)); - DBG((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + DBGM(("cia_machine_check: vector=0x%lx la_ptr=0x%lx\n", vector, la_ptr)); + DBGM((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", regs->pc, mchk_header->size, mchk_header->proc_offset, mchk_header->sys_offset)); - DBG(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", + DBGM(("cia_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", CIA_mcheck_expected, mchk_sysdata->epic_dcsr, mchk_sysdata->epic_pear)); -#ifdef DEBUG +#ifdef DEBUG_MCHECK { unsigned long *ptr; int i; ptr = (unsigned long *)la_ptr; for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { - printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + printk(" +%lx %lx %lx\n", i*sizeof(long), + ptr[i], ptr[i+1]); } } -#endif /* DEBUG */ +#endif /* DEBUG_MCHECK */ /* * Check if machine check is due to a badaddr() and if so, * ignore the machine check. */ mb(); mb(); - if (CIA_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { - DBG(("CIA machine check expected\n")); + if (CIA_mcheck_expected) { + DBGM(("CIA machine check expected\n")); CIA_mcheck_expected = 0; CIA_mcheck_taken = 1; mb(); @@ -483,12 +614,58 @@ cia_pci_clr_err(); wrmces(0x7); mb(); + return; + } + + switch ((unsigned int) mchk_header->code) { + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "generic hard error"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; + case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; + case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* system specific (these are for Alcor, at least): */ + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x205: reason = "parity error detected by CIA"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: + sprintf(buf, "reason for machine-check unknown (0x%x)", + (unsigned int) mchk_header->code); + reason = buf; + break; + } + wrmces(rdmces()); /* reset machine check pending flag */ + mb(); + + printk(KERN_CRIT " CIA machine check: %s%s\n", + reason, mchk_header->retry ? " (retryable)" : ""); + printk(KERN_CRIT " vector=0x%lx la_ptr=0x%lx pc=0x%lx\n", + vector, la_ptr, regs->pc); + + /* dump the logout area to give all info: */ + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); } -#if 1 - else - printk("CIA machine check NOT expected\n") ; -#endif -#endif } #endif /* CONFIG_ALPHA_CIA */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S --- v2.0.33/linux/arch/alpha/kernel/entry.S Thu Nov 21 03:41:46 1996 +++ linux/arch/alpha/kernel/entry.S Wed Jun 3 15:17:46 1998 @@ -4,6 +4,7 @@ * kernel entry-points */ +#include #include #define halt .long PAL_halt @@ -22,7 +23,7 @@ /* * stack offsets */ -#define SP_OFF 160 +#define SP_OFF 184 #define SWITCH_STACK_SIZE 320 @@ -47,9 +48,11 @@ * regs 9-15 preserved by C code * regs 16-18 saved by PAL-code * regs 29-30 saved and set up by PAL-code + * JRP - Save regs 16-18 in a special area of the stack, so that + * the palcode-provided values are available to the signal handler. */ #define SAVE_ALL \ - subq $30,160,$30; \ + subq $30,184,$30; \ stq $0,0($30); \ stq $1,8($30); \ stq $2,16($30); \ @@ -71,7 +74,10 @@ stq $28,144($30); \ lda $2,hae; \ ldq $2,HAE_CACHE($2); \ - stq $2,152($30) + stq $2,152($30); \ + stq $16,160($30); \ + stq $17,168($30); \ + stq $18,176($30) #define RESTORE_ALL \ lda $8,hae; \ @@ -107,11 +113,11 @@ ldq $26,128($30); \ ldq $27,136($30); \ ldq $28,144($30); \ - addq $30,160,$30 + addq $30,184,$30 .text .set noat -#ifdef __linux__ +#if defined(__linux__) && !defined(__ELF__) .set singlegp #endif @@ -160,6 +166,36 @@ .globl entIF .ent entIF entIF: +#ifdef CONFIG_KGDB + bne $16,1f /* not a bpt trap -> */ + /* + * Call kgdb if it's enabled and if "current" is not being + * traced or if we get a bpt in kernel mode (the architecture + * manual defines the values of $17 and $18 as "unpredictable", + * so they are fair game). + */ + lda $17,kgdb_enabled + ldl $17,0($17) + beq $17,1f /* kgdb not enabled -> */ + +#ifndef CONFIG_ALPHA_CABRIOLET + /* + * MILO on Cabriolet doesn't seem to setup the PS in the + * PALframe correctly. (davidm@azstarnet.com) + */ + ldq $18,0($30) /* get ps */ + and $18,8,$18 + beq $18,entKGDB +#endif + + lda $17,current_set + ldq $17,0($17) + bis $31,PF_PTRACED,$18 + ldq $17,TASK_FLAGS($17) + and $17,$18,$17 + beq $17,entKGDB +1: +#endif SAVE_ALL lda $27,do_entIF lda $26,ret_from_sys_call @@ -759,3 +795,127 @@ .quad sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, do_entSys /* sys_afs_syscall */, sys_newuname .quad sys_nanosleep, sys_mremap, do_entSys, do_entSys, do_entSys .quad sys_pciconfig_read, sys_pciconfig_write, do_entSys, do_entSys, do_entSys + + +#ifdef CONFIG_KGDB + +#define KGDB_STACK_SIZE (8*1024) + + .lcomm kgdb_stack_bottom, KGDB_STACK_SIZE + +/* + * The following is the nasty part of entering KGDB. GDB assumes that + * it can access the memory below the interrupted thread's stack + * pointer (SP). This means that the stub cannot run on this stack + * (interrupts could overwrite changes made by GDB). Similarly, we do + * not want to bother the GDB user with the call chain leading from + * the interrupt handler to this function. That is, the SP reported + * to GDB should be the thread's SP at the point it was interrupted. We + * achieve all this by switching to KGDB's own stack before saving any + * registers (of course, the PAL-generated frame is already on the stack and + * we copy that part explicitly). GDB does whatever it wants to the + * thread's stack; and when it continues execution, we restore the + * registers from the private stack and return from the interrupt. + * + * This is not re-entrant---gdb on the host probably would get confused + * anyway, but we probably ought to detect that case at the least... + */ +.align 3 +.globl entKGDB +.ent entKGDB +entKGDB: + lda $16,kgdb_stack_bottom + lda $16,(KGDB_STACK_SIZE-48)($16) + bis $31,$30,$17 /* save real sp in a1 */ + bis $31,$16,$30 /* update sp atomically! */ + SAVE_ALL /* save regular stuff */ + /* + * Only now can we disable interrupts (swpipl may step on + * t0,t8..t11, and a0): + */ + bis $31,7,$16 + call_pal PAL_swpipl + + /* copy PAL frame to kgdb stack: */ + + ldq $1,0($17) + ldq $2,8($17) + ldq $3,16($17) + ldq $4,24($17) + ldq $5,32($17) + ldq $6,40($17) + + stq $1,0+184($30) + stq $2,8+184($30) + stq $3,16+184($30) + stq $4,24+184($30) + stq $5,32+184($30) + stq $6,40+184($30) + + /* allocate space for and save caller saved regs as well as orig sp: */ + + lda $30,-320($30) + addq $17,48,$17 /* pop PAL frame */ + stq $17,0($30) /* save original sp */ + + stq $9,0x08($30); stq $10,0x10($30); stq $11,0x18($30) + stq $12,0x20($30); stq $13,0x28($30); stq $14,0x30($30) + stq $15,0x38($30) + + stt $f0,0x40($30); stt $f1,0x48($30); stt $f2,0x50($30); stt $f3,0x58($30) + stt $f4,0x60($30); stt $f5,0x68($30); stt $f6,0x70($30); stt $f7,0x78($30) + stt $f8,0x80($30); stt $f9,0x88($30); stt $f10,0x90($30); stt $f11,0x98($30) + stt $f12,0xa0($30); stt $f13,0xa8($30); stt $f14,0xb0($30); stt $f15,0xb8($30) + stt $f16,0xc0($30); stt $f17,0xc8($30); stt $f18,0xd0($30); stt $f19,0xd8($30) + stt $f20,0xe0($30); stt $f21,0xe8($30); stt $f22,0xf0($30); stt $f23,0xf8($30) + stt $f24,0x100($30); stt $f25,0x108($30); stt $f26,0x110($30); stt $f27,0x118($30) + stt $f28,0x120($30); stt $f29,0x128($30); stt $f30,0x130($30); stt $f31,0x138($30) + + bis $31,$30,$16 + + lda $27,kgdb_handle_exception + jsr $26,($27),kgdb_handle_exception + + ldq $17,0($30) /* load new sp and caller-saved registers */ + + ldq $9,0x08($30); ldq $10,0x10($30); ldq $11,0x18($30) + ldq $12,0x20($30); ldq $13,0x28($30); ldq $14,0x30($30) + ldq $15,0x38($30) + + ldt $f0,0x40($30); ldt $f1,0x48($30); ldt $f2,0x50($30); ldt $f3,0x58($30) + ldt $f4,0x60($30); ldt $f5,0x68($30); ldt $f6,0x70($30); ldt $f7,0x78($30) + ldt $f8,0x80($30); ldt $f9,0x88($30); ldt $f10,0x90($30); ldt $f11,0x98($30) + ldt $f12,0xa0($30); ldt $f13,0xa8($30); ldt $f14,0xb0($30); ldt $f15,0xb8($30) + ldt $f16,0xc0($30); ldt $f17,0xc8($30); ldt $f18,0xd0($30); ldt $f19,0xd8($30) + ldt $f20,0xe0($30); ldt $f21,0xe8($30); ldt $f22,0xf0($30); ldt $f23,0xf8($30) + ldt $f24,0x100($30); ldt $f25,0x108($30); ldt $f26,0x110($30); ldt $f27,0x118($30) + ldt $f28,0x120($30); ldt $f29,0x128($30); ldt $f30,0x130($30); ldt $f31,0x138($30) + lda $30,320($30) /* pop extra register frame */ + + /* + * Copy PAL frame from kgdb stack to new stack so we can rti + * to it: + */ + ldq $1,0+184($30) + ldq $2,8+184($30) + ldq $3,16+184($30) + ldq $4,24+184($30) + ldq $5,32+184($30) + ldq $6,40+184($30) + + subq $17,48,$17 /* alloc space for PAL frame on new stack */ + + stq $1,0($17) + stq $2,8($17) + stq $3,16($17) + stq $4,24($17) + stq $5,32($17) + stq $6,40($17) + + RESTORE_ALL + bis $31,$17,$30 /* establish new sp */ + rti + +.end entKGDB + +#endif /* CONFIG_KGDB */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/head.S linux/arch/alpha/kernel/head.S --- v2.0.33/linux/arch/alpha/kernel/head.S Mon Jul 1 10:06:05 1996 +++ linux/arch/alpha/kernel/head.S Wed Jun 3 15:17:46 1998 @@ -92,6 +92,50 @@ ret ($26) .end wrmces + .align 3 + .globl whami + .ent whami +whami: + call_pal PAL_whami + ret ($26) + .end whami + + .align 3 + .globl wripir + .ent wripir +wripir: + call_pal PAL_wripir + ret ($26) + .end wripir + + .align 3 + .globl cserve_ena + .ent cserve_ena +cserve_ena: + lda $30,-0x08($30) + stq $17,0($30) + bis $16,$16,$17 + lda $16,52($31) + call_pal PAL_cserve + ldq $17,0($30) + lda $30,0x08($30) + ret ($26) + .end cserve_ena + + .align 3 + .globl cserve_dis + .ent cserve_dis +cserve_dis: + lda $30,-0x08($30) + stq $17,0($30) + bis $16,$16,$17 + lda $16,53($31) + call_pal PAL_cserve + ldq $17,0($30) + lda $30,0x08($30) + ret ($26) + .end cserve_dis + # # The following two functions don't need trapb/excb instructions # around the mf_fpcr/mt_fpcr instructions because (a) the kernel diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c --- v2.0.33/linux/arch/alpha/kernel/irq.c Mon Oct 13 08:28:55 1997 +++ linux/arch/alpha/kernel/irq.c Wed Jun 3 15:17:46 1998 @@ -27,6 +27,7 @@ #include extern void timer_interrupt(struct pt_regs * regs); +extern void cserve_update_hw(unsigned long, unsigned long); #if NR_IRQS > 64 # error Unable to handle more than 64 irq levels. @@ -42,7 +43,8 @@ * 0.. 7 first (E)ISA PIC (irq level 0..7) * 8..15 second (E)ISA PIC (irq level 8..15) * Systems with PCI interrupt lines managed by GRU (e.g., Alcor, XLT): - * 16..47 PCI interrupts 0..31 (int at GRU_INT_MASK) + * or PYXIS (e.g. Miata, PC164-LX) + * 16..47 PCI interrupts 0..31 (xxx_INT_MASK reg) * Mikasa: * 16..31 PCI interrupts 0..15 (short at I/O port 536) * Other systems (not Mikasa) with 16 PCI interrupt lines: @@ -50,13 +52,43 @@ * 24..31 PCI interrupts 8..15 (char at I/O port 27) * Systems with 17 PCI interrupt lines (e.g., Cabriolet and eb164): * 16..32 PCI interrupts 0..31 (int at I/O port 804) + * Takara: + * 16..19 PCI interrupts A thru D + * For SABLE, which is really baroque, we manage 40 IRQ's, but the + * hardware really only supports 24, not via normal ISA PIC, + * but cascaded custom 8259's, etc. + * 0-7 (char at 536) + * 8-15 (char at 53a) + * 16-23 (char at 53c) */ static unsigned long irq_mask = ~0UL; +#ifdef CONFIG_ALPHA_SABLE +/* note that the vector reported by the SRM PALcode corresponds to the + interrupt mask bits, but we have to manage via more normal IRQs */ +static char sable_irq_to_mask[NR_IRQS] = { + -1, 6, -1, 8, 15, 12, 7, 9, /* pseudo PIC 0-7 */ + -1, 16, 17, 18, 3, -1, 21, 22, /* pseudo PIC 8-15 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 0-7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* pseudo EISA 8-15 */ + 2, 1, 0, 4, 5, -1, -1, -1, /* pseudo PCI */ + }; +#define IRQ_TO_MASK(irq) (sable_irq_to_mask[(irq)]) +static char sable_mask_to_irq[NR_IRQS] = { + 34, 33, 32, 12, 35, 36, 1, 6, /* mask 0-7 */ + 3, 7, -1, -1, 5, -1, -1, 4, /* mask 8-15 */ + 9, 10, 11, -1, -1, 14, 15, -1, /* mask 16-23 */ + }; +#else /* CONFIG_ALPHA_SABLE */ +#define IRQ_TO_MASK(irq) (irq) +#endif /* CONFIG_ALPHA_SABLE */ /* * Update the hardware with the irq mask passed in MASK. The function * exploits the fact that it is known that only bit IRQ has changed. + * + * There deliberately isn't a case in here for the Takara since it + * wouldn't work anyway because the interrupt controller is bizarre. */ static void update_hw(unsigned long irq, unsigned long mask) { @@ -65,32 +97,111 @@ mask |= 0x7ff00000UL << 16; #endif switch (irq) { -#if NR_IRQS == 48 - default: + +#ifdef CONFIG_ALPHA_SABLE + /* SABLE does everything different, so we manage it that way... :-( */ + /* the "irq" argument is really the mask bit number */ + case 0 ... 7: + outb(mask, 0x537); + break; + case 8 ... 15: + outb(mask >> 8, 0x53b); + break; + case 16 ... 23: + outb(mask >> 16, 0x53d); + break; +#else /* SABLE */ + +#if defined(CONFIG_ALPHA_NORITAKE) + /* note inverted sense of mask bits: */ + case 16 ... 31: + outw(~(mask >> 16), 0x54a); + break; + case 32 ... 47: + outw(~(mask >> 32), 0x54c); + break; +#endif /* NORITAKE */ + +#if defined(CONFIG_ALPHA_MIATA) + case 16 ... 47: + { unsigned long temp; + /* note inverted sense of mask bits: */ + /* make CERTAIN none of the bogus ints get enabled */ + *(unsigned long *)PYXIS_INT_MASK = + ~((long)mask >> 16) & ~0x400000000000063bUL; mb(); + temp = *(unsigned long *)PYXIS_INT_MASK; + break; + } +#endif /* MIATA */ + +#if defined(CONFIG_ALPHA_RUFFIAN) + case 16 ... 47: + { unsigned long temp; + /* note inverted sense of mask bits: */ + /* make CERTAIN none of the bogus ints get enabled */ + *(unsigned long *)PYXIS_INT_MASK = + ~((long)mask >> 16) & 0x00000000ffffffbfUL; mb(); + temp = *(unsigned long *)PYXIS_INT_MASK; + break; + } +#endif /* RUFFIAN */ + +#if defined(CONFIG_ALPHA_SX164) + case 16 ... 39: +#if defined(CONFIG_ALPHA_SRM) + cserve_update_hw(irq, mask); + break; +#else + { unsigned long temp; + /* make CERTAIN none of the bogus ints get enabled */ + *(unsigned long *)PYXIS_INT_MASK = + ~((long)mask >> 16) & ~0x000000000000003bUL; mb(); + temp = *(unsigned long *)PYXIS_INT_MASK; + break; + } +#endif /* SRM */ +#endif /* SX164 */ + +#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) + case 16 ... 47: /* note inverted sense of mask bits: */ *(unsigned int *)GRU_INT_MASK = ~(mask >> 16); mb(); break; +#endif /* ALCOR || XLT */ -#elif NR_IRQS == 33 - default: - outl(mask >> 16, 0x804); +#if defined(CONFIG_ALPHA_CABRIOLET) || \ + defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_EB164) || \ + defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) +#if defined(CONFIG_ALPHA_SRM) + case 16 ... 34: + cserve_update_hw(irq, mask); + break; +#else /* SRM */ + case 16 ... 34: + outl(irq_mask >> 16, 0x804); break; +#endif /* SRM */ +#endif /* CABRIO || EB66P || EB164 || PC164 || LX164 */ -#elif defined(CONFIG_ALPHA_MIKASA) - default: +#if defined(CONFIG_ALPHA_MIKASA) + case 16 ... 31: outw(~(mask >> 16), 0x536); /* note invert */ break; +#endif /* MIKASA */ -#elif NR_IRQS == 32 +#if defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) case 16 ... 23: outb(mask >> 16, 0x26); break; - - default: + case 24 ... 31: outb(mask >> 24, 0x27); break; -#endif +#endif /* EB66 || EB64P */ + /* handle ISA irqs last---fast devices belong on PCI... */ + /* this is common for all except SABLE! */ case 0 ... 7: /* ISA PIC1 */ outb(mask, 0x21); @@ -99,7 +210,10 @@ case 8 ...15: /* ISA PIC2 */ outb(mask >> 8, 0xA1); break; - } + +#endif /* SABLE */ + + } /* end switch (irq) */ } static inline void mask_irq(unsigned long irq) @@ -120,7 +234,7 @@ save_flags(flags); cli(); - mask_irq(irq_nr); + mask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } @@ -130,7 +244,7 @@ save_flags(flags); cli(); - unmask_irq(irq_nr); + unmask_irq(IRQ_TO_MASK(irq_nr)); restore_flags(flags); } @@ -164,7 +278,32 @@ static inline void ack_irq(int irq) { +#ifdef CONFIG_ALPHA_SABLE + /* note that the "irq" here is really the mask bit number */ + switch (irq) { + case 0 ... 7: + outb(0xE0 | (irq - 0), 0x536); + outb(0xE0 | 1, 0x534); /* slave 0 */ + break; + case 8 ... 15: + outb(0xE0 | (irq - 8), 0x53a); + outb(0xE0 | 3, 0x534); /* slave 1 */ + break; + case 16 ... 24: + outb(0xE0 | (irq - 16), 0x53c); + outb(0xE0 | 4, 0x534); /* slave 2 */ + break; + } +#else /* CONFIG_ALPHA_SABLE */ if (irq < 16) { +#if defined(CONFIG_ALPHA_RUFFIAN) + /* ack pyxis ISA interrupt */ + *(unsigned long *)PYXIS_INT_REQ = (0x01UL << 7); + if (irq > 7) { + outb(0x20,0xa0); + } + outb(0x20,0x20); +#else /* RUFFIAN */ /* ACK the interrupt making it the lowest priority */ /* First the slave .. */ if (irq > 7) { @@ -178,7 +317,16 @@ *(int *)GRU_INT_CLEAR = 0x80000000; mb(); *(int *)GRU_INT_CLEAR = 0x00000000; mb(); #endif /* ALCOR || XLT */ +#endif /* RUFFIAN */ } +#if defined(CONFIG_ALPHA_RUFFIAN) + else { + /* ack pyxis int */ + *(unsigned long *)PYXIS_INT_REQ = (1UL << (irq-16)); + } +#endif /* RUFFIAN */ + +#endif /* CONFIG_ALPHA_SABLE */ } int request_irq(unsigned int irq, @@ -236,7 +384,7 @@ *p = action; if (!shared) - unmask_irq(irq); + unmask_irq(IRQ_TO_MASK(irq)); restore_flags(flags); return 0; @@ -264,7 +412,7 @@ cli(); *p = action->next; if (!irq[irq_action]) - mask_irq(irq); + mask_irq(IRQ_TO_MASK(irq)); restore_flags(flags); kfree(action); return; @@ -323,25 +471,39 @@ struct irqaction * action; if ((unsigned) irq > NR_IRQS) { - printk("device_interrupt: unexpected interrupt %d\n", irq); + printk("device_interrupt: illegal interrupt %d\n", irq); + return; + } + +#if defined(CONFIG_ALPHA_RUFFIAN) + /* RUFFIAN uses ISA IRQ #0 to deliver clock ticks */ + if (irq == 0) { + timer_interrupt(regs); + ack_irq(0); return; } +#endif /* RUFFIAN */ kstat.interrupts[irq]++; action = irq_action[irq]; + /* * For normal interrupts, we mask it out, and then ACK it. * This way another (more timing-critical) interrupt can * come through while we're doing this one. * - * Note! A irq without a handler gets masked and acked, but + * Note! An irq without a handler gets masked and acked, but * never unmasked. The autoirq stuff depends on this (it looks * at the masks before and after doing the probing). */ mask_irq(ack); ack_irq(ack); - if (!action) + if (!action) { +#if 1 + printk("device_interrupt: unexpected interrupt %d\n", irq); +#endif return; + } if (action->flags & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); do { @@ -365,6 +527,8 @@ # define IACK_SC LCA_IACK_SC #elif defined(CONFIG_ALPHA_CIA) # define IACK_SC CIA_IACK_SC +#elif defined(CONFIG_ALPHA_PYXIS) +# define IACK_SC PYXIS_IACK_SC #else /* * This is bogus but necessary to get it to compile @@ -554,8 +718,185 @@ restore_flags(flags); } +#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164) +/* we have to conditionally compile this because of PYXIS_xxx symbols */ +static inline void miata_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ + unsigned long pld, tmp; + unsigned int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* read the interrupt summary register of PYXIS */ + pld = (*(volatile unsigned long *)PYXIS_INT_REQ); + +#if 0 + printk("[0x%08lx/0x%08lx/0x%04x]", pld, + *(volatile unsigned long *)PYXIS_INT_MASK, + inb(0x20) | (inb(0xA0) << 8)); +#endif + +#ifdef CONFIG_ALPHA_MIATA + /* for now, AND off any bits we are not interested in: */ + /* HALT (2), timer (6), ISA Bridge (7), 21142 (8) */ + /* then all the PCI slots/INTXs (12-31) */ +/* maybe HALT should only be used for SRM console boots? */ + pld &= 0x00000000fffff9c4UL; +#endif /* MIATA */ +#ifdef CONFIG_ALPHA_SX164 + /* for now, AND off any bits we are not interested in: */ + /* HALT (2), timer (6), ISA Bridge (7) */ + /* then all the PCI slots/INTXs (8-23) */ +/* HALT should only be used for SRM console boots */ + pld &= 0x0000000000ffffc0UL; +#endif /* SX164 */ + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 7) { + isa_device_interrupt(vector, regs); + } else if (i == 6) + continue; + else { /* if not timer int */ + device_interrupt(16 + i, 16 + i, regs); + } + *(unsigned long *)PYXIS_INT_REQ = 1UL << i; mb(); + tmp = *(volatile unsigned long *)PYXIS_INT_REQ; + } + restore_flags(flags); +} +#endif /* MIATA || SX164 */ + +static inline void noritake_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ + unsigned long pld; + unsigned int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* read the interrupt summary registers */ + /* read the interrupt summary registers of NORITAKE */ + pld = ((unsigned long) inw(0x544) << 32) | + ((unsigned long) inw(0x542) << 16) | + ((unsigned long) inb(0xa0) << 8) | + ((unsigned long) inb(0x20)); + +#if 0 + printk("[0x%08lx]", pld); +#endif + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i < 16) { + isa_device_interrupt(vector, regs); + } else { + device_interrupt(i, i, regs); + } + } + restore_flags(flags); +} + #endif /* CONFIG_PCI */ +#if defined(CONFIG_ALPHA_RUFFIAN) +static inline void ruffian_device_interrupt(unsigned long vector, + struct pt_regs * regs) + +{ + unsigned long pld, tmp; + unsigned int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* read the interrupt summary register of PYXIS */ + pld = (*(volatile unsigned long *)PYXIS_INT_REQ); + + /* for now, AND off any bits we are not interested in: + * HALT (2), timer (6), ISA Bridge (7), 21142 (8) + * then all the PCI slots/INTXs (12-31) + * flash(5) :DWH: + */ + pld &= 0x00000000ffffff9fUL;/* was ffff7f */ + + /* + * Now for every possible bit set, work through them and call + * the appropriate interrupt handler. + */ + + while (pld) { + i = ffz(~pld); + pld &= pld - 1; /* clear least bit set */ + if (i == 7) { + isa_device_interrupt(vector, regs); + } else { /* if not timer int */ + device_interrupt(16 + i,16 + i,regs); + } + *(unsigned long *)PYXIS_INT_REQ = 1UL << i; mb(); + tmp = *(volatile unsigned long *)PYXIS_INT_REQ; + } + restore_flags(flags); +} +#endif /* RUFFIAN */ + +static inline void takara_device_interrupt(unsigned long vector, + struct pt_regs * regs) +{ + unsigned long flags; + unsigned intstatus; + + save_flags(flags); + cli(); + + /* + * The PALcode will have passed us vectors 0x800 or 0x810, + * which are fairly arbitrary values and serve only to tell + * us whether an interrupt has come in on IRQ0 or IRQ1. If + * it's IRQ1 it's a PCI interrupt; if it's IRQ0, it's + * probably ISA, but PCI interrupts can come through IRQ0 + * as well if the interrupt controller isn't in accelerated + * mode. + * + * OTOH, the accelerator thing doesn't seem to be working + * overly well, so what we'll do instead is try directly + * examining the Master Interrupt Register to see if it's a + * PCI interrupt, and if _not_ then we'll pass it on to the + * ISA handler. + */ + + intstatus = inw(0x500) & 15; + if (intstatus) { + /* + * This is a PCI interrupt. Check each bit and + * despatch an interrupt if it's set. + */ + if (intstatus & 8) device_interrupt(16+3, 16+3, regs); + if (intstatus & 4) device_interrupt(16+2, 16+2, regs); + if (intstatus & 2) device_interrupt(16+1, 16+1, regs); + if (intstatus & 1) device_interrupt(16+0, 16+0, regs); + } else + isa_device_interrupt (vector, regs); + + restore_flags(flags); +} + /* * Jensen is special: the vector is 0x8X0 for EISA interrupt X, and * 0x9X0 for the local motherboard interrupts.. @@ -587,6 +928,9 @@ save_flags(flags); cli(); +#if 0 +printk("srm_device_interrupt: vector 0x%lx\n", vector); +#endif ack = irq = (vector - 0x800) >> 4; @@ -608,11 +952,69 @@ irq = 7; #endif /* CONFIG_ALPHA_JENSEN */ +#ifdef CONFIG_ALPHA_MIATA + /* + * I really hate to do this, but the MIATA SRM console ignores the + * low 8 bits in the interrupt summary register, and reports the + * vector 0x80 *lower* than I expected from the bit numbering in + * the documentation. + * This was done because the low 8 summary bits really aren't used + * for reporting any interrupts (the PCI-ISA bridge, bit 7, isn't + * used for this purpose, as PIC interrupts are delivered as the + * vectors 0x800-0x8f0). + * But I really don't want to change the fixup code for allocation + * of IRQs, nor the irq_mask maintenance stuff, both of which look + * nice and clean now. + * So, here's this grotty hack... :-( + */ + if (irq >= 16) + ack = irq = irq + 8; +#endif /* CONFIG_ALPHA_MIATA */ + +#ifdef CONFIG_ALPHA_NORITAKE + /* + * I really hate to do this, but the NORITAKE SRM console reports + * PCI vectors *lower* than I expected from the bit numbering in + * the documentation. + * But I really don't want to change the fixup code for allocation + * of IRQs, nor the irq_mask maintenance stuff, both of which look + * nice and clean now. + * So, here's this grotty hack... :-( + */ + if (irq >= 16) + ack = irq = irq + 1; +#endif /* CONFIG_ALPHA_NORITAKE */ + +#ifdef CONFIG_ALPHA_SABLE + irq = sable_mask_to_irq[(ack)]; +#if 0 + if (irq == 5 || irq == 9 || irq == 10 || irq == 11 || + irq == 14 || irq == 15) + printk("srm_device_interrupt: vector=0x%lx ack=0x%x irq=0x%x\n", + vector, ack, irq); +#endif +#endif /* CONFIG_ALPHA_SABLE */ + device_interrupt(irq, ack, regs); restore_flags(flags) ; } +/* PROBE_MASK is the bitset of irqs that we consider for autoprobing: */ +#if defined(CONFIG_ALPHA_P2K) + /* always mask out unused timer irq 0 and RTC irq 8 */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0x101UL) +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) + /* always mask out unused timer irq 0, "irqs" 20-30, and the EISA cascade: */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~0xfff000000001UL) +#elif defined(CONFIG_ALPHA_RUFFIAN) + /* must leave timer irq 0 in the mask */ +# define PROBE_MASK ((1UL << NR_IRQS) - 1) +#else + /* always mask out unused timer irq 0: */ +# define PROBE_MASK (((1UL << NR_IRQS) - 1) & ~1UL) +#endif + /* * Start listening for interrupts.. */ @@ -624,10 +1026,13 @@ unsigned int i; for (i = NR_IRQS - 1; i > 0; i--) { + if (!(PROBE_MASK & (1UL << i))) { + continue; + } action = irq_action[i]; if (!action) { enable_irq(i); - irqs |= (1 << i); + irqs |= (1UL << i); } } /* @@ -650,10 +1055,7 @@ { int i; - irqs &= irq_mask & ~1; /* always mask out irq 0---it's the unused timer */ -#ifdef CONFIG_ALPHA_P2K - irqs &= ~(1 << 8); /* mask out irq 8 since that's the unused RTC input to PIC */ -#endif + irqs &= irq_mask; if (!irqs) return 0; i = ffz(~irqs); @@ -676,6 +1078,14 @@ extern void cia_machine_check(unsigned long vector, unsigned long la, struct pt_regs * regs); cia_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_PYXIS) + extern void pyxis_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); + pyxis_machine_check(vector, la, regs); +#elif defined(CONFIG_ALPHA_T2) + extern void t2_machine_check(unsigned long vector, unsigned long la, + struct pt_regs * regs); + t2_machine_check(vector, la, regs); #else printk("Machine check\n"); #endif @@ -685,6 +1095,9 @@ unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) { +#if 0 +printk("do_entInt: type 0x%lx\n", type); +#endif switch (type) { case 0: printk("Interprocessor interrupt? You must be kidding\n"); @@ -699,17 +1112,30 @@ #if defined(CONFIG_ALPHA_JENSEN) || defined(CONFIG_ALPHA_NONAME) || \ defined(CONFIG_ALPHA_P2K) || defined(CONFIG_ALPHA_SRM) srm_device_interrupt(vector, ®s); -#elif NR_IRQS == 48 +#else /* everyone else */ + +#if defined(CONFIG_ALPHA_MIATA) || defined(CONFIG_ALPHA_SX164) + miata_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_NORITAKE) + noritake_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) alcor_and_xlt_device_interrupt(vector, ®s); -#elif NR_IRQS == 33 +#elif defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_EB164) || defined(CONFIG_ALPHA_PC164) || \ + defined(CONFIG_ALPHA_LX164) cabriolet_and_eb66p_device_interrupt(vector, ®s); #elif defined(CONFIG_ALPHA_MIKASA) mikasa_device_interrupt(vector, ®s); -#elif NR_IRQS == 32 +#elif defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) eb66_and_eb64p_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_RUFFIAN) + ruffian_device_interrupt(vector, ®s); +#elif defined(CONFIG_ALPHA_TAKARA) + takara_device_interrupt(vector, ®s); #elif NR_IRQS == 16 isa_device_interrupt(vector, ®s); #endif +#endif /* everyone else */ return; case 4: printk("Performance counter interrupt\n"); @@ -724,23 +1150,141 @@ void init_IRQ(void) { + unsigned int temp; + wrent(entInt, 0); - dma_outb(0, DMA1_RESET_REG); - dma_outb(0, DMA2_RESET_REG); - dma_outb(0, DMA1_CLR_MASK_REG); - dma_outb(0, DMA2_CLR_MASK_REG); -#if NR_IRQS == 48 + + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + +/* FIXME FIXME FIXME FIXME FIXME */ +#if !defined(CONFIG_ALPHA_SX164) + outb(0, DMA1_CLR_MASK_REG); + /* we need to figure out why this fails on the SX164 */ + outb(0, DMA2_CLR_MASK_REG); +#endif /* !SX164 */ +/* end FIXMEs */ + +#if defined(CONFIG_ALPHA_SABLE) + outb(irq_mask , 0x537); /* slave 0 */ + outb(irq_mask >> 8, 0x53b); /* slave 1 */ + outb(irq_mask >> 16, 0x53d); /* slave 2 */ + outb(0x44, 0x535); /* enable cascades in master */ +#else /* everybody but SABLE */ + +#if defined(CONFIG_ALPHA_MIATA) + /* note invert on MASK bits */ + *(unsigned long *)PYXIS_INT_MASK = + ~((long)irq_mask >> 16) & ~0x400000000000063bUL; mb(); +#if 0 + /* these break on MiataGL so we'll try not to do it at all */ + *(unsigned long *)PYXIS_INT_HILO = 0x000000B2UL; mb();/* ISA/NMI HI */ + *(unsigned long *)PYXIS_RT_COUNT = 0UL; mb();/* clear count */ +#endif + /* clear upper timer */ + *(unsigned long *)PYXIS_INT_REQ = 0x4000000000000180UL; mb(); + + /* Send -INTA pulses to clear any pending interrupts ...*/ + temp = *(volatile unsigned int *) IACK_SC; + + enable_irq(16 + 2); /* enable HALT switch - SRM only? */ + enable_irq(16 + 6); /* enable timer */ + enable_irq(16 + 7); /* enable ISA PIC cascade */ +#endif /* MIATA */ + +#if defined(CONFIG_ALPHA_SX164) +#if !defined(CONFIG_ALPHA_SRM) + /* note invert on MASK bits */ + *(unsigned long *)PYXIS_INT_MASK = ~((long)irq_mask >> 16); mb(); +#if 0 + *(unsigned long *)PYXIS_INT_HILO = 0x000000B2UL; mb();/* ISA/NMI HI */ + *(unsigned long *)PYXIS_RT_COUNT = 0UL; mb();/* clear count */ +#endif +#endif /* !SRM */ + enable_irq(16 + 6); /* enable timer */ + enable_irq(16 + 7); /* enable ISA PIC cascade */ +#endif /* SX164 */ + +#if defined(CONFIG_ALPHA_NORITAKE) + outw(~(irq_mask >> 16), 0x54a); /* note invert */ + outw(~(irq_mask >> 32), 0x54c); /* note invert */ +#endif /* NORITAKE */ + +#if defined(CONFIG_ALPHA_ALCOR) || defined(CONFIG_ALPHA_XLT) *(unsigned int *)GRU_INT_MASK = ~(irq_mask >> 16); mb();/* invert */ + *(unsigned int *)GRU_INT_EDGE = 0UL; mb();/* all are level */ + *(unsigned int *)GRU_INT_HILO = 0x80000000UL; mb();/* ISA only HI */ + *(unsigned int *)GRU_INT_CLEAR = 0UL; mb();/* all clear */ enable_irq(16 + 31); /* enable (E)ISA PIC cascade */ -#elif NR_IRQS == 33 +#endif /* ALCOR || XLT */ + +#if defined(CONFIG_ALPHA_CABRIOLET) || defined(CONFIG_ALPHA_EB66P) || \ + defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) || \ + defined(CONFIG_ALPHA_EB164) +#if !defined(CONFIG_ALPHA_SRM) outl(irq_mask >> 16, 0x804); +#endif /* !SRM */ + /* Send -INTA pulses to clear any pending interrupts ...*/ + temp = *(volatile unsigned int *) IACK_SC; enable_irq(16 + 4); /* enable SIO cascade */ -#elif defined(CONFIG_ALPHA_MIKASA) +#endif /* CABRIO || EB66P || PC164 || LX164 || EB164 */ + +#if defined(CONFIG_ALPHA_MIKASA) outw(~(irq_mask >> 16), 0x536); /* note invert */ -#elif NR_IRQS == 32 +#endif /* MIKASA */ + +#if defined(CONFIG_ALPHA_EB66) || defined(CONFIG_ALPHA_EB64P) outb(irq_mask >> 16, 0x26); outb(irq_mask >> 24, 0x27); enable_irq(16 + 5); /* enable SIO cascade */ -#endif - enable_irq(2); /* enable cascade */ +#endif /* EB66 || EB64P */ + +#if defined(CONFIG_ALPHA_RUFFIAN) + /* invert 6&7 for i82371 */ + *(unsigned long *)PYXIS_INT_HILO = 0x000000c0UL;mb(); + *(unsigned long *)PYXIS_INT_CNFG = 0x00002064UL;mb(); /* all clear */ + *(unsigned long *)PYXIS_INT_MASK = ((long)0x00000000UL);mb(); + *(unsigned long *)PYXIS_INT_REQ = 0xffffffffUL;mb(); + + outb(0x11,0xA0); + outb(0x08,0xA1); + outb(0x02,0xA1); + outb(0x01,0xA1); + outb(0xFF,0xA1); + + outb(0x11,0x20); + outb(0x00,0x21); + outb(0x04,0x21); + outb(0x01,0x21); + outb(0xFF,0x21); + + /* Send -INTA pulses to clear any pending interrupts ...*/ + temp = *(volatile unsigned int *) IACK_SC; + + /* Finish writing the 82C59A PIC Operation Control Words */ + outb(0x20,0xA0); + outb(0x20,0x20); + + /* Turn on the interrupt controller, the timer interrupt */ + enable_irq(16 + 7); /* enable ISA PIC cascade */ + enable_irq(0); /* enable timer */ +#endif /* RUFFIAN */ + +#ifdef CONFIG_ALPHA_TAKARA + { + unsigned int ctlreg = inl(0x500); + ctlreg &= ~0x8000; /* return to non-accelerated mode */ + outw(ctlreg >> 16, 0x502); + outw(ctlreg & 0xFFFF, 0x500); + ctlreg = 0x05107c00; /* enable the PCI interrupt register */ + printk("Setting to 0x%08x\n", ctlreg); + outw(ctlreg >> 16, 0x502); + outw(ctlreg & 0xFFFF, 0x500); + } +#endif /* TAKARA */ + + /* and finally, everyone but SABLE does this */ + enable_irq(2); /* enable 2nd PIC cascade */ + +#endif /* SABLE */ } diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/lca.c linux/arch/alpha/kernel/lca.c --- v2.0.33/linux/arch/alpha/kernel/lca.c Sun Jul 14 23:47:41 1996 +++ linux/arch/alpha/kernel/lca.c Wed Jun 3 15:17:46 1998 @@ -22,6 +22,7 @@ #ifdef CONFIG_ALPHA_LCA #define vulp volatile unsigned long * +#define vuip volatile unsigned int * /* * Machine check reasons. Defined according to PALcode sources @@ -46,6 +47,11 @@ #define MCHK_K_SIO_IOCHK 0x206 /* all platforms so far */ #define MCHK_K_DCSR 0x208 /* all but Noname */ +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int LCA_DMA_WIN_BASE = LCA_DMA_WIN_BASE_DEFAULT; +unsigned int LCA_DMA_WIN_SIZE = LCA_DMA_WIN_SIZE_DEFAULT; +#endif /* SRM_SETUP */ + /* * Given a bus, device, and function number, compute resulting * configuration space address and setup the LCA_IOC_CONF register @@ -289,6 +295,40 @@ unsigned long lca_init(unsigned long mem_start, unsigned long mem_end) { +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if ((*(vulp)LCA_IOC_W_BASE0 & (1UL<<33)) && + (*(vulp)LCA_IOC_T_BASE0 == 0)) + { + LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE0 & 0xffffffffUL; + LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK0 & 0xffffffffUL; + LCA_DMA_WIN_SIZE += 1; +#if 1 + printk("lca_init: using Window 0 settings\n"); + printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)LCA_IOC_W_BASE0, + *(vulp)LCA_IOC_W_MASK0, + *(vulp)LCA_IOC_T_BASE0); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if ((*(vulp)LCA_IOC_W_BASE1 & (1UL<<33)) && + (*(vulp)LCA_IOC_T_BASE1 == 0)) + { + LCA_DMA_WIN_BASE = *(vulp)LCA_IOC_W_BASE1 & 0xffffffffUL; + LCA_DMA_WIN_SIZE = *(vulp)LCA_IOC_W_MASK1 & 0xffffffffUL; + LCA_DMA_WIN_SIZE += 1; +#if 1 + printk("lca_init: using Window 1 settings\n"); + printk("lca_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)LCA_IOC_W_BASE1, + *(vulp)LCA_IOC_W_MASK1, + *(vulp)LCA_IOC_T_BASE1); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { /* * Set up the PCI->physical memory translation windows. * For now, window 1 is disabled. In the future, we may @@ -296,9 +336,11 @@ * goes at 1 GB and is 1 GB large. */ *(vulp)LCA_IOC_W_BASE1 = 0UL<<33; + *(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE; *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1; *(vulp)LCA_IOC_T_BASE0 = 0; + } /* * Disable PCI parity for now. The NCR53c810 chip has @@ -386,11 +428,14 @@ void lca_machine_check (unsigned long vector, unsigned long la, struct pt_regs *regs) { + unsigned long * ptr; const char * reason; union el_lca el; char buf[128]; + long i; - printk("lca: machine check (la=0x%lx,pc=0x%lx)\n", la, regs->pc); + printk(KERN_CRIT "lca: machine check (la=0x%lx,pc=0x%lx)\n", + la, regs->pc); el.c = (struct el_common *) la; /* * The first quadword after the common header always seems to @@ -399,9 +444,9 @@ * logout frame, the upper 32 bits is the machine check * revision level, which we ignore for now. */ - switch (el.s->reason & 0xffffffff) { + switch (el.c->code & 0xffffffff) { case MCHK_K_TPERR: reason = "tag parity error"; break; - case MCHK_K_TCPERR: reason = "tag something parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; case MCHK_K_HERR: reason = "access to non-existent memory"; break; case MCHK_K_ECC_C: reason = "correctable ECC error"; break; case MCHK_K_ECC_NC: reason = "non-correctable ECC error"; break; @@ -416,7 +461,7 @@ case MCHK_K_UNKNOWN: default: sprintf(buf, "reason for machine-check unknown (0x%lx)", - el.s->reason & 0xffffffff); + el.c->code & 0xffffffff); reason = buf; break; } @@ -425,7 +470,8 @@ switch (el.c->size) { case sizeof(struct el_lca_mcheck_short): - printk(" Reason: %s (short frame%s, dc_stat=%lx):\n", + printk(KERN_CRIT + " Reason: %s (short frame%s, dc_stat=%lx):\pn", reason, el.c->retry ? ", retryable" : "", el.s->dc_stat); if (el.s->esr & ESR_EAV) { mem_error(el.s->esr, el.s->ear); @@ -436,11 +482,12 @@ break; case sizeof(struct el_lca_mcheck_long): - printk(" Reason: %s (long frame%s):\n", + printk(KERN_CRIT " Reason: %s (long frame%s):\n", reason, el.c->retry ? ", retryable" : ""); - printk(" reason: %lx exc_addr: %lx dc_stat: %lx\n", + printk(KERN_CRIT + " reason: %lx exc_addr: %lx dc_stat: %lx\n", el.l->pt[0], el.l->exc_addr, el.l->dc_stat); - printk(" car: %lx\n", el.l->car); + printk(KERN_CRIT " car: %lx\n", el.l->car); if (el.l->esr & ESR_EAV) { mem_error(el.l->esr, el.l->ear); } @@ -450,8 +497,55 @@ break; default: - printk(" Unknown errorlog size %d\n", el.c->size); + printk(KERN_CRIT " Unknown errorlog size %d\n", el.c->size); + } + + /* dump the logout area to give all info: */ + + ptr = (unsigned long *) la; + for (i = 0; i < el.c->size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); } +} + +void +lca_clock_print(void) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + + printk("Status of clock control:\n"); + printk("\tPrimary clock divisor\t0x%x\n", GET_PRIMARY(pmr_reg)); + printk("\tOverride clock divisor\t0x%x\n", GET_OVERRIDE(pmr_reg)); + printk("\tInterrupt override is %s\n", + (pmr_reg & LCA_PMR_INTO) ? "on" : "off"); + printk("\tDMA override is %s\n", + (pmr_reg & LCA_PMR_DMAO) ? "on" : "off"); + +} + +int +lca_get_clock(void) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + return(GET_PRIMARY(pmr_reg)); + + } + +void +lca_clock_fiddle(int divisor) +{ + long pmr_reg; + + pmr_reg = READ_PMR; + SET_PRIMARY_CLOCK(pmr_reg, divisor); +/* lca_norm_clock = divisor; */ + WRITE_PMR(pmr_reg); + mb(); } #endif /* CONFIG_ALPHA_LCA */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c --- v2.0.33/linux/arch/alpha/kernel/process.c Tue Aug 20 23:18:07 1996 +++ linux/arch/alpha/kernel/process.c Wed Jun 3 15:17:46 1998 @@ -32,8 +32,9 @@ #include #include -asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, +asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, struct pt_regs regs) { (®s)->hae = hae; @@ -52,8 +53,42 @@ } } +#include + +static void swap_context(struct thread_struct * pcb) +{ + __asm__ __volatile__( + "bis %0,%0,$16\n\t" + "call_pal %1\n\t" + : /* no outputs */ + : "r" (pcb), "i" (PAL_swpctx) + : "$0", "$1", "$16", "$22", "$23", "$24", "$25"); +} + void hard_reset_now(void) { +#if defined(CONFIG_ALPHA_SRM_SETUP) + extern void reset_for_srm(void); + extern struct hwrpb_struct *hwrpb; + extern struct thread_struct *original_pcb_ptr; + struct percpu_struct *cpup; + unsigned long flags; + + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset); + flags = cpup->flags; +#if 1 + printk("hard_reset_now: flags 0x%lx\n", flags); +#endif + flags &= ~0x0000000000ff0001UL; /* clear reason to "default" */ + flags |= 0x0000000000020000UL; /* this is "cold bootstrap" */ +/* flags |= 0x0000000000030000UL; *//* this is "warm bootstrap" */ +/* flags |= 0x0000000000040000UL; *//* this is "remain halted" */ + cpup->flags = flags; + mb(); + reset_for_srm(); + swap_context(original_pcb_ptr); +#endif #if defined(CONFIG_ALPHA_SRM) && defined(CONFIG_ALPHA_ALCOR) /* who said DEC engineer's have no sense of humor? ;-)) */ *(int *) GRU_RESET = 0x0000dead; diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/pyxis.c linux/arch/alpha/kernel/pyxis.c --- v2.0.33/linux/arch/alpha/kernel/pyxis.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/pyxis.c Wed Jun 3 15:17:46 1998 @@ -0,0 +1,670 @@ +/* + * Code common to all PYXIS chips. + * + * Based on code written by David A Rusling (david.rusling@reo.mts.dec.com). + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_ALPHA_PYXIS + +#ifdef DEBUG +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +#define DEBUG_MCHECK +#ifdef DEBUG_MCHECK +# define DBG_MCK(args) printk args +/* #define DEBUG_MCHECK_DUMP */ +#else +# define DBG_MCK(args) +#endif + +#define vuip volatile unsigned int * +#define vulp volatile unsigned long * + +static volatile unsigned int PYXIS_mcheck_expected = 0; +static volatile unsigned int PYXIS_mcheck_taken = 0; +static unsigned int PYXIS_jd; + +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int PYXIS_DMA_WIN_BASE = PYXIS_DMA_WIN_BASE_DEFAULT; +unsigned int PYXIS_DMA_WIN_SIZE = PYXIS_DMA_WIN_SIZE_DEFAULT; +unsigned long pyxis_sm_base_r1, pyxis_sm_base_r2, pyxis_sm_base_r3; +#endif /* SRM_SETUP */ + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the PYXIS_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + DBG(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p, type1=0x%p)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device; + + device = device_fn >> 3; + /* type 0 configuration cycle: */ +#if NOT_NOW + if (device > 20) { + DBG(("mk_conf_addr: device (%d) > 20, returning -1\n", + device)); + return -1; + } +#endif + *type1 = 0; + addr = (device_fn << 8) | (where); + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + + +static unsigned int conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, value, temp; + unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + + /* reset status register to avoid losing errors: */ + stat0 = *((vuip)PYXIS_ERR); + *((vuip)PYXIS_ERR) = stat0; mb(); + temp = *((vuip)PYXIS_ERR); + DBG(("conf_read: PYXIS ERR was 0x%x\n", stat0)); + /* if Type1 access, must set PYXIS CFG */ + if (type1) { + pyxis_cfg = *((vuip)PYXIS_CFG); + *((vuip)PYXIS_CFG) = pyxis_cfg | 1; mb(); + temp = *((vuip)PYXIS_CFG); + DBG(("conf_read: TYPE1 access\n")); + } + + mb(); + draina(); + PYXIS_mcheck_expected = 1; + PYXIS_mcheck_taken = 0; + mb(); + /* access configuration space: */ + value = *((vuip)addr); + mb(); + mb(); + if (PYXIS_mcheck_taken) { + PYXIS_mcheck_taken = 0; + value = 0xffffffffU; + mb(); + } + PYXIS_mcheck_expected = 0; + mb(); + + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *((vuip)PYXIS_CFG) = pyxis_cfg & ~1; mb(); + temp = *((vuip)PYXIS_CFG); + } + + DBG(("conf_read(): finished\n")); + + restore_flags(flags); + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, temp; + unsigned int pyxis_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + /* reset status register to avoid losing errors: */ + stat0 = *((vuip)PYXIS_ERR); + *((vuip)PYXIS_ERR) = stat0; mb(); + temp = *((vuip)PYXIS_ERR); + DBG(("conf_write: PYXIS ERR was 0x%x\n", stat0)); + /* if Type1 access, must set PYXIS CFG */ + if (type1) { + pyxis_cfg = *((vuip)PYXIS_CFG); + *((vuip)PYXIS_CFG) = pyxis_cfg | 1; mb(); + temp = *((vuip)PYXIS_CFG); + DBG(("conf_read: TYPE1 access\n")); + } + + draina(); + PYXIS_mcheck_expected = 1; + mb(); + /* access configuration space: */ + *((vuip)addr) = value; + mb(); + mb(); + temp = *((vuip)PYXIS_ERR); /* do a PYXIS read to force the write */ + PYXIS_mcheck_expected = 0; + mb(); + + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *((vuip)PYXIS_CFG) = pyxis_cfg & ~1; mb(); + temp = *((vuip)PYXIS_CFG); + } + + DBG(("conf_write(): finished\n")); + restore_flags(flags); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffffffff; + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + *value = conf_read(addr, type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x00; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x08; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = PYXIS_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long pyxis_init(unsigned long mem_start, unsigned long mem_end) +{ + unsigned int pyxis_err; + +#if 0 +printk("pyxis_init: PYXIS_ERR_MASK 0x%x\n", *(vuip)PYXIS_ERR_MASK); +printk("pyxis_init: PYXIS_ERR 0x%x\n", *(vuip)PYXIS_ERR); + +printk("pyxis_init: PYXIS_INT_REQ 0x%lx\n", *(vulp)PYXIS_INT_REQ); +printk("pyxis_init: PYXIS_INT_MASK 0x%lx\n", *(vulp)PYXIS_INT_MASK); +printk("pyxis_init: PYXIS_INT_ROUTE 0x%lx\n", *(vulp)PYXIS_INT_ROUTE); +printk("pyxis_init: PYXIS_INT_HILO 0x%lx\n", *(vulp)PYXIS_INT_HILO); +printk("pyxis_init: PYXIS_INT_CNFG 0x%x\n", *(vuip)PYXIS_INT_CNFG); +printk("pyxis_init: PYXIS_RT_COUNT 0x%lx\n", *(vulp)PYXIS_RT_COUNT); +#endif + +#if 0 +printk("pyxis_init: W0 BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W0_BASE, *(vuip)PYXIS_W0_MASK, *(vuip)PYXIS_T0_BASE); +printk("pyxis_init: W1 BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W1_BASE, *(vuip)PYXIS_W1_MASK, *(vuip)PYXIS_T1_BASE); +printk("pyxis_init: W2 BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W2_BASE, *(vuip)PYXIS_W2_MASK, *(vuip)PYXIS_T2_BASE); +printk("pyxis_init: W3 BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W3_BASE, *(vuip)PYXIS_W3_MASK, *(vuip)PYXIS_T3_BASE); +#endif + + /* + * Set up error reporting. Make sure CPU_PE is OFF in the mask. + */ + pyxis_err = *(vuip)PYXIS_ERR_MASK; + pyxis_err &= ~4; + *(vuip)PYXIS_ERR_MASK = pyxis_err; + mb(); + pyxis_err = *(vuip)PYXIS_ERR_MASK; + + pyxis_err = *(vuip)PYXIS_ERR ; + pyxis_err |= 0x180; /* master/target abort */ + *(vuip)PYXIS_ERR = pyxis_err ; + mb() ; + pyxis_err = *(vuip)PYXIS_ERR ; + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 0 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W0_BASE & 3) == 1) && + (*(vuip)PYXIS_T0_BASE == 0) && + ((*(vuip)PYXIS_W0_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W0_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W0_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 0 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W0_BASE, + *(vuip)PYXIS_W0_MASK, + *(vuip)PYXIS_T0_BASE); +#endif + } + else /* check window 1 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W1_BASE & 3) == 1) && + (*(vuip)PYXIS_T1_BASE == 0) && + ((*(vuip)PYXIS_W1_MASK & 0xfff00000U) > 0x0ff00000U)) +{ + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W1_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W1_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 1 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W1_BASE, + *(vuip)PYXIS_W1_MASK, + *(vuip)PYXIS_T1_BASE); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W2_BASE & 3) == 1) && + (*(vuip)PYXIS_T2_BASE == 0) && + ((*(vuip)PYXIS_W2_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W2_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W2_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 2 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W2_BASE, + *(vuip)PYXIS_W2_MASK, + *(vuip)PYXIS_T2_BASE); +#endif + } + else /* check window 3 for enabled and mapped to 0 */ + if (((*(vuip)PYXIS_W3_BASE & 3) == 1) && + (*(vuip)PYXIS_T3_BASE == 0) && + ((*(vuip)PYXIS_W3_MASK & 0xfff00000U) > 0x0ff00000U)) + { + PYXIS_DMA_WIN_BASE = *(vuip)PYXIS_W3_BASE & 0xfff00000U; + PYXIS_DMA_WIN_SIZE = *(vuip)PYXIS_W3_MASK & 0xfff00000U; + PYXIS_DMA_WIN_SIZE += 0x00100000U; +#if 1 + printk("pyxis_init: using Window 3 settings\n"); + printk("pyxis_init: BASE 0x%x MASK 0x%x TRANS 0x%x\n", + *(vuip)PYXIS_W3_BASE, + *(vuip)PYXIS_W3_MASK, + *(vuip)PYXIS_T3_BASE); +#endif + } + else /* we must use our defaults which were pre-initialized... */ +#endif /* SRM_SETUP */ + { +#if defined(CONFIG_ALPHA_RUFFIAN) +#if 1 + printk("pyxis_init: skipping window register rewrites... " + "trust DeskStation firmware!\n"); +#endif +#else /* RUFFIAN */ + /* + * Set up the PCI->physical memory translation windows. + * For now, windows 1,2 and 3 are disabled. In the future, we may + * want to use them to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + + *(vuip)PYXIS_W0_BASE = 1U | (PYXIS_DMA_WIN_BASE & 0xfff00000U); + *(vuip)PYXIS_W0_MASK = (PYXIS_DMA_WIN_SIZE - 1) & 0xfff00000U; + *(vuip)PYXIS_T0_BASE = 0; + + *(vuip)PYXIS_W1_BASE = 0x0 ; + *(vuip)PYXIS_W2_BASE = 0x0 ; + *(vuip)PYXIS_W3_BASE = 0x0 ; + mb(); +#endif /* RUFFIAN */ + } + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("PYXIS_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + + /* + * Next, clear the PYXIS_CFG register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + { + unsigned int pyxis_cfg, temp; + pyxis_cfg = *((vuip)PYXIS_CFG); mb(); + if (pyxis_cfg != 0) { +#if 1 + printk("PYXIS_init: CFG was 0x%x\n", pyxis_cfg); +#endif + *((vuip)PYXIS_CFG) = 0; mb(); + temp = *((vuip)PYXIS_CFG); + } + } + + { + unsigned int pyxis_hae_mem = *((vuip)PYXIS_HAE_MEM); + unsigned int pyxis_hae_io = *((vuip)PYXIS_HAE_IO); +#if 0 + printk("PYXIS_init: HAE_MEM was 0x%x\n", pyxis_hae_mem); + printk("PYXIS_init: HAE_IO was 0x%x\n", pyxis_hae_io); +#endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + sigh... For the SRM setup, unless we know apriori what the HAE + contents will be, we need to setup the arbitrary region bases + so we can test against the range of addresses and tailor the + region chosen for the SPARSE memory access. + + see include/asm-alpha/pyxis.h for the SPARSE mem read/write + */ + pyxis_sm_base_r1 = (pyxis_hae_mem ) & 0xe0000000UL;/* region 1 */ + pyxis_sm_base_r2 = (pyxis_hae_mem << 16) & 0xf8000000UL;/* region 2 */ + pyxis_sm_base_r3 = (pyxis_hae_mem << 24) & 0xfc000000UL;/* region 3 */ +#else /* SRM_SETUP */ + *((vuip)PYXIS_HAE_MEM) = 0U; mb(); + pyxis_hae_mem = *((vuip)PYXIS_HAE_MEM); + *((vuip)PYXIS_HAE_IO) = 0; mb(); + pyxis_hae_io = *((vuip)PYXIS_HAE_IO); +#endif /* SRM_SETUP */ + } + + /* + * Finally, check that the PYXIS_CTRL1 has IOA_BEN set for + * enabling byte/word PCI bus space(s) access. + */ + { + unsigned int ctrl1; + ctrl1 = *((vuip) PYXIS_CTRL1); + if (!(ctrl1 & 1)) { +#if 0 + printk("PYXIS_init: enabling byte/word PCI space\n"); +#endif + *((vuip) PYXIS_CTRL1) = ctrl1 | 1; mb(); + ctrl1 = *((vuip)PYXIS_CTRL1); + } + } + + return mem_start; +} + +int pyxis_pci_clr_err(void) +{ + PYXIS_jd = *((vuip)PYXIS_ERR); + DBG(("PYXIS_pci_clr_err: PYXIS ERR after read 0x%x\n", PYXIS_jd)); + *((vuip)PYXIS_ERR) = 0x0180; mb(); + PYXIS_jd = *((vuip)PYXIS_ERR); + return 0; +} + +void pyxis_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ +#if 0 + printk("PYXIS machine check ignored\n") ; +#else + struct el_common *mchk_header; + struct el_PYXIS_sysdata_mcheck *mchk_sysdata; + + mchk_header = (struct el_common *)la_ptr; + + mchk_sysdata = + (struct el_PYXIS_sysdata_mcheck *)(la_ptr + mchk_header->sys_offset); + +#if 0 + DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + DBG_MCK(("pyxis_machine_check: expected %d DCSR 0x%lx PEAR 0x%lx\n", + PYXIS_mcheck_expected, mchk_sysdata->epic_dcsr, + mchk_sysdata->epic_pear)); +#endif +#ifdef DEBUG_MCHECK_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->size / sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_MCHECK_DUMP */ + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + mb(); + if (PYXIS_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + DBG(("PYXIS machine check expected\n")); + PYXIS_mcheck_expected = 0; + PYXIS_mcheck_taken = 1; + mb(); + mb(); + draina(); + pyxis_pci_clr_err(); + wrmces(0x7); + mb(); + } +#if 1 + else { + printk("PYXIS machine check NOT expected\n") ; + DBG_MCK(("pyxis_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + DBG_MCK(("\t\t pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->size, mchk_header->proc_offset, + mchk_header->sys_offset)); + PYXIS_mcheck_expected = 0; + PYXIS_mcheck_taken = 1; + mb(); + mb(); + draina(); + pyxis_pci_clr_err(); + wrmces(0x7); + mb(); + } +#endif +#endif +} + +#if defined(CONFIG_ALPHA_RUFFIAN) +/* NOTE: this is only used by MILO, AFAIK... */ +/* + * The DeskStation Ruffian motherboard firmware does not place + * the memory size in the PALimpure area. Therefore, it uses + * the Bank Configuration Registers in PYXIS to obtain the size. + */ +unsigned long pyxis_get_bank_size(unsigned long offset) +{ + unsigned long bank_addr; + + /* valid offsets are: 0x800, 0x840 and 0x880 + * since Ruffian only uses three banks + */ + bank_addr = (unsigned long)PYXIS_MCR + offset; + + /* check BANK_ENABLE */ + if (*((unsigned long *)bank_addr) && 0x01) + { + /* do case on BANK_SIZE bits */ + switch (*((unsigned long *)bank_addr) & 0x01e) + { + case 0x00: return 0x40000000UL; break; /* 1G */ + case 0x02: return 0x20000000UL; break; /* 512M */ + case 0x04: return 0x10000000UL; break; /* 256M */ + case 0x06: return 0x08000000UL; break; /* 128M */ + case 0x08: return 0x04000000UL; break; /* 64M */ + case 0x0a: return 0x02000000UL; break; /* 32M */ + case 0x0c: return 0x01000000UL; break; /* 16M */ + case 0x0e: return 0x00800000UL; break; /* 8M */ + case 0x10: return 0x80000000UL; break; /* 2G */ + default : return 0x00000000UL; break; /* ERROR*/ + } + } else + return 0x00UL; +} +#endif /* CONFIG_ALPHA_RUFFIAN */ + +#endif /* CONFIG_ALPHA_PYXIS */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c --- v2.0.33/linux/arch/alpha/kernel/setup.c Tue Aug 20 23:18:07 1996 +++ linux/arch/alpha/kernel/setup.c Wed Jun 3 15:17:46 1998 @@ -22,6 +22,7 @@ #include #include #include /* CONFIG_ALPHA_LCA etc */ +#include #include #include @@ -58,13 +59,26 @@ * code think we're on a VGA color display. */ struct screen_info screen_info = { +#if defined(CONFIG_ALPHA_BOOK1) + /* the AlphaBook1 has LCD video fixed at 800x600, 37 rows and 100 cols */ + 0, 37, /* orig-x, orig-y */ +#else 0, 25, /* orig-x, orig-y */ +#endif { 0, 0 }, /* unused */ 0, /* orig-video-page */ 0, /* orig-video-mode */ +#if defined(CONFIG_ALPHA_BOOK1) + 100, /* orig-video-cols */ +#else 80, /* orig-video-cols */ +#endif 0,0,0, /* ega_ax, ega_bx, ega_cx */ +#if defined(CONFIG_ALPHA_BOOK1) + 37, /* orig-video-lines */ +#else 25, /* orig-video-lines */ +#endif 1, /* orig-video-isVGA */ 16 /* orig-video-points */ }; @@ -84,9 +98,12 @@ outb(0x18, 0x41); #endif +#if !defined(CONFIG_ALPHA_RUFFIAN) + /* Ruffian depends on the system timer established in MILO!! */ outb(0x36, 0x43); /* counter 0: system timer */ outb(0x00, 0x40); outb(0x00, 0x40); +#endif /* RUFFIAN */ outb(0xb6, 0x43); /* counter 2: speaker */ outb(0x31, 0x42); @@ -121,9 +138,19 @@ init_pit(); + if ((CMOS_READ(RTC_FREQ_SELECT) & 0x3f) != 0x26) { +#if 1 + printk("init_timers: setting RTC_FREQ to 1024/sec\n"); +#endif + CMOS_WRITE(0x26, RTC_FREQ_SELECT); + } + hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr); +#ifndef CONFIG_ALPHA_SRM_SETUP set_hae(hae.cache); /* sync HAE register w/hae_cache */ +#endif /* !SRM_SETUP */ + wrmces(0x7); /* reset enable correctable error reports */ ROOT_DEV = to_kdev_t(0x0802); /* sda2 */ @@ -154,39 +181,161 @@ *memory_start_p = apecs_init(*memory_start_p, *memory_end_p); #elif defined(CONFIG_ALPHA_CIA) *memory_start_p = cia_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_PYXIS) + *memory_start_p = pyxis_init(*memory_start_p, *memory_end_p); +#elif defined(CONFIG_ALPHA_T2) + *memory_start_p = t2_init(*memory_start_p, *memory_end_p); #endif } -/* - * BUFFER is PAGE_SIZE bytes long. +# define N(a) (sizeof(a)/sizeof(a[0])) + +/* A change was made to the HWRPB via an ECO and the following code tracks + * a part of the ECO. The HWRPB version must be 5 or higher or the ECO + * was not implemented in the console firmware. If its at rev 5 or greater + * we can get the platform ascii string name from the HWRPB. Thats what this + * function does. It checks the rev level and if the string is in the HWRPB + * it returns the addtess of the string ... a pointer to the platform name. + * + * Returns: + * - Pointer to a ascii string if its in the HWRPB + * - Pointer to a blank string if the data is not in the HWRPB. */ +static char * +platform_string(void) +{ + struct dsr_struct *dsr; + static char unk_system_string[] = "N/A"; + + /* Go to the console for the string pointer. + * If the rpb_vers is not 5 or greater the rpb + * is old and does not have this data in it. + */ + if (hwrpb->revision < 5) + return (unk_system_string); + else { + /* The Dynamic System Recognition struct + * has the system platform name starting + * after the character count of the string. + */ + dsr = ((struct dsr_struct *) + ((char *)hwrpb + hwrpb->dsr_offset)); + return ((char *)dsr + (dsr->sysname_off + + sizeof(long))); + } +} + +static void +get_sysnames(long type, long variation, + char **type_name, char **variation_name) +{ + static char *sys_unknown = "Unknown"; + static char *systype_names[] = { + "0", + "ADU", "Cobra", "Ruby", "Flamingo", "Mannequin", "Jensen", + "Pelican", "Morgan", "Sable", "Medulla", "Noname", + "Turbolaser", "Avanti", "14", "Alcor", "Tradewind", + "Mikasa", "EB64", "EB66", "EB64+", "AlphaBook1", + "Rawhide", "K2", "Lynx", "XL", "EB164", "Noritake", + "Cortex", "29", "Miata", "XXM", "Takara", "Yukon", + "Tsunami", "Wildfire", "CUSCO" + }; + + static char *unofficial_names[] = { + "100", + "Ruffian" + }; + + static char * eb164_names[] = {"EB164", "PC164", "LX164", "SX164"}; + static int eb164_indices[] = {0,0,0,1,1,1,1,1,2,2,2,2,3,3,3,3}; + + static char * alcor_names[] = {"Alcor", "Maverick", "Bret"}; + static int alcor_indices[] = {0,0,0,1,1,1,0,0,0,0,0,0,2,2,2,2,2,2}; + + static char * eb64p_names[] = {"EB64+", "Cabriolet", "AlphaPCI64"}; + static int eb64p_indices[] = {0,0,1.2}; + + static char * eb66_names[] = {"EB66", "EB66+"}; + static int eb66_indices[] = {0,0,1}; + + static char * rawhide_names[] = {"Dodge", "Wrangler", "Durango", + "Tincup", "DaVinci"}; + static int rawhide_indices[] = {0,0,0,1,1,2,2,3,3,4,4}; + + long member; + + + /* restore real CABRIO and EB66+ family names, ie EB64+ and EB66 */ + if (type < 0) type = -type; + + /* if not in the tables, make it UNKNOWN */ + /* else set type name to family */ + if (type < N(systype_names)) { + *type_name = systype_names[type]; + } else + if ((type > ST_UNOFFICIAL_BIAS) && + (type - ST_UNOFFICIAL_BIAS) < N(unofficial_names)) { + *type_name = unofficial_names[type - ST_UNOFFICIAL_BIAS]; + } else { + *type_name = sys_unknown; + *variation_name = sys_unknown; + return; + } + + /* set variation to "0"; if variation is zero, done */ + *variation_name = systype_names[0]; + if (variation == 0) { + return; + } + + member = (variation >> 10) & 0x3f; /* member ID is a bit-field */ + + switch (type) { /* select by family */ + default: /* default to variation "0" ????FIXME???? */ + break; + case ST_DEC_EB164: + if (member < N(eb164_indices)) + *variation_name = eb164_names[eb164_indices[member]]; + break; + case ST_DEC_ALCOR: + if (member < N(alcor_indices)) + *variation_name = alcor_names[alcor_indices[member]]; + break; + case ST_DEC_EB64P: + if (member < N(eb64p_indices)) + *variation_name = eb64p_names[eb64p_indices[member]]; + break; + case ST_DEC_EB66: + if (member < N(eb66_indices)) + *variation_name = eb66_names[eb66_indices[member]]; + break; + case ST_DEC_RAWHIDE: + if (member < N(rawhide_indices)) + *variation_name = rawhide_names[rawhide_indices[member]]; + break; + } /* end family switch */ + return; +} + int get_cpuinfo(char *buffer) +/* BUFFER is PAGE_SIZE bytes long. */ { const char *cpu_name[] = { - "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45" - }; -# define SYSTYPE_NAME_BIAS 20 - const char *systype_name[] = { - "Cabriolet", "EB66P", "-18", "-17", "-16", "-15", - "-14", "-13", "-12", "-11", "-10", "-9", "-8", - "-7", "-6", "-5", "-4", "-3", "-2", "-1", "0", - "ADU", "Cobra", "Ruby", "Flamingo", "5", "Jensen", - "Pelican", "8", "Sable", "AXPvme", "Noname", - "Turbolaser", "Avanti", "Mustang", "Alcor", "16", - "Mikasa", "18", "EB66", "EB64+", "21", "22", "23", - "24", "25", "EB164" + "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45", "EV56", + "EV6", "PCA56" }; struct percpu_struct *cpu; unsigned int cpu_index; - long sysname_index; + char *systype_name; + char *sysvariation_name; extern struct unaligned_stat { unsigned long count, va, pc; } unaligned[2]; -# define N(a) (sizeof(a)/sizeof(a[0])) cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); cpu_index = (unsigned) (cpu->type - 1); - sysname_index = hwrpb->sys_type + SYSTYPE_NAME_BIAS; + get_sysnames(hwrpb->sys_type, hwrpb->sys_variation, + &systype_name, &sysvariation_name); return sprintf(buffer, "cpu\t\t\t: Alpha\n" @@ -195,7 +344,7 @@ "cpu revision\t\t: %ld\n" "cpu serial number\t: %s\n" "system type\t\t: %s\n" - "system variation\t: %ld\n" + "system variation\t: %s\n" "system revision\t\t: %ld\n" "system serial number\t: %s\n" "cycle frequency [Hz]\t: %lu\n" @@ -205,14 +354,13 @@ "max. addr. space #\t: %ld\n" "BogoMIPS\t\t: %lu.%02lu\n" "kernel unaligned acc\t: %ld (pc=%lx,va=%lx)\n" - "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n", + "user unaligned acc\t: %ld (pc=%lx,va=%lx)\n" + "platform string\t\t: %s\n", (cpu_index < N(cpu_name) ? cpu_name[cpu_index] : "Unknown"), cpu->variation, cpu->revision, (char*)cpu->serial_no, - (sysname_index < N(systype_name) - ? systype_name[sysname_index] : "Unknown"), - hwrpb->sys_variation, hwrpb->sys_revision, + systype_name, sysvariation_name, hwrpb->sys_revision, (char*)hwrpb->ssn, hwrpb->cycle_freq, hwrpb->intr_freq / 4096, @@ -222,6 +370,7 @@ hwrpb->max_asn, loops_per_sec / 500000, (loops_per_sec / 5000) % 100, unaligned[0].count, unaligned[0].pc, unaligned[0].va, - unaligned[1].count, unaligned[1].pc, unaligned[1].va); -# undef N + unaligned[1].count, unaligned[1].pc, unaligned[1].va, + platform_string()); } +# undef N diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c --- v2.0.33/linux/arch/alpha/kernel/signal.c Mon Aug 4 15:41:48 1997 +++ linux/arch/alpha/kernel/signal.c Wed Jun 3 15:17:46 1998 @@ -203,6 +203,9 @@ put_fs_quad(regs->gp , sc->sc_regs+29); for (i = 0; i < 31; i++) put_fs_quad(sw->fp[i], sc->sc_fpregs+i); + put_fs_quad(regs->trap_a0, &sc->sc_traparg_a0); + put_fs_quad(regs->trap_a1, &sc->sc_traparg_a1); + put_fs_quad(regs->trap_a2, &sc->sc_traparg_a2); /* * The following is: diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/smc.c linux/arch/alpha/kernel/smc.c --- v2.0.33/linux/arch/alpha/kernel/smc.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/smc.c Wed Jun 3 15:17:46 1998 @@ -0,0 +1,2842 @@ +/* + * SMC 37C93X and 37C669 initialization code + */ +#include +#include + +#if 0 +# define DBG_DEVS(args) printk args +#else +# define DBG_DEVS(args) +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#define KB 1024 +#define MB (1024*KB) +#define GB (1024*MB) + +#if defined(CONFIG_ALPHA_PC164) || defined(CONFIG_ALPHA_LX164) + +/* device "activate" register contents */ +#define DEVICE_ON 1 +#define DEVICE_OFF 0 + +/* configuration on/off keys */ +#define CONFIG_ON_KEY 0x55 +#define CONFIG_OFF_KEY 0xaa + +/* configuration space device definitions */ +#define FDC 0 +#define IDE1 1 +#define IDE2 2 +#define PARP 3 +#define SER1 4 +#define SER2 5 +#define RTCL 6 +#define KYBD 7 +#define AUXIO 8 + +/* Chip register offsets from base */ +#define CONFIG_CONTROL 0x02 +#define INDEX_ADDRESS 0x03 +#define LOGICAL_DEVICE_NUMBER 0x07 +#define DEVICE_ID 0x20 +#define DEVICE_REV 0x21 +#define POWER_CONTROL 0x22 +#define POWER_MGMT 0x23 +#define OSC 0x24 + +#define ACTIVATE 0x30 +#define ADDR_HI 0x60 +#define ADDR_LO 0x61 +#define INTERRUPT_SEL 0x70 +#define INTERRUPT_SEL_2 0x72 /* KYBD/MOUS only */ +#define DMA_CHANNEL_SEL 0x74 /* FDC/PARP only */ + +#define FDD_MODE_REGISTER 0x90 +#define FDD_OPTION_REGISTER 0x91 + +/* values that we read back that are expected ... */ +#define VALID_DEVICE_ID 2 + +/* default device addresses */ +#define KYBD_INTERRUPT 1 +#define MOUS_INTERRUPT 12 +#define COM2_BASE 0x2f8 +#define COM2_INTERRUPT 3 +#define COM1_BASE 0x3f8 +#define COM1_INTERRUPT 4 +#define PARP_BASE 0x3bc +#define PARP_INTERRUPT 7 + +#define SMC_DEBUG 0 + +unsigned long SMCConfigState( unsigned long baseAddr ) +{ + unsigned char devId; + unsigned char devRev; + + unsigned long configPort; + unsigned long indexPort; + unsigned long dataPort; + + configPort = indexPort = baseAddr; + dataPort = ( unsigned long )( ( char * )configPort + 1 ); + + outb(CONFIG_ON_KEY, configPort); + outb(CONFIG_ON_KEY, configPort); + outb(DEVICE_ID, indexPort); + devId = inb(dataPort); + if ( devId == VALID_DEVICE_ID ) { + outb(DEVICE_REV, indexPort); + devRev = inb(dataPort); + } + else { + baseAddr = 0; + } + return( baseAddr ); +} + +void SMCRunState( unsigned long baseAddr ) +{ + outb(CONFIG_OFF_KEY, baseAddr); +} + +unsigned long SMCDetectUltraIO(void) +{ + unsigned long baseAddr; + + baseAddr = 0x3F0; + if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x3F0 ) { + return( baseAddr ); + } + baseAddr = 0x370; + if ( ( baseAddr = SMCConfigState( baseAddr ) ) == 0x370 ) { + return( baseAddr ); + } + return( ( unsigned long )0 ); +} + +void SMCEnableDevice( unsigned long baseAddr, + unsigned long device, + unsigned long portaddr, + unsigned long interrupt) +{ + unsigned long indexPort; + unsigned long dataPort; + + indexPort = baseAddr; + dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(device, dataPort); + + outb(ADDR_LO, indexPort); + outb(( portaddr & 0xFF ), dataPort); + + outb(ADDR_HI, indexPort); + outb(( ( portaddr >> 8 ) & 0xFF ), dataPort); + + outb(INTERRUPT_SEL, indexPort); + outb(interrupt, dataPort); + + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); +} + +void SMCEnableKYBD( unsigned long baseAddr ) +{ + unsigned long indexPort; + unsigned long dataPort; + + indexPort = baseAddr; + dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(KYBD, dataPort); + + outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ + outb(KYBD_INTERRUPT, dataPort); + + outb(INTERRUPT_SEL_2, indexPort);/* Secondary interrupt select */ + outb(MOUS_INTERRUPT, dataPort); + + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); +} + +void SMCEnableFDC( unsigned long baseAddr ) +{ + unsigned long indexPort; + unsigned long dataPort; + + unsigned char oldValue; + + indexPort = baseAddr; + dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + + outb(LOGICAL_DEVICE_NUMBER, indexPort); + outb(FDC, dataPort); + + outb(FDD_MODE_REGISTER, indexPort); + oldValue = inb(dataPort); + + oldValue |= 0x0E; /* Enable burst mode */ + outb(oldValue, dataPort); + + outb(INTERRUPT_SEL, indexPort); /* Primary interrupt select */ + outb(0x06, dataPort ); + + outb(DMA_CHANNEL_SEL, indexPort); /* DMA channel select */ + outb(0x02, dataPort); + + outb(ACTIVATE, indexPort); + outb(DEVICE_ON, dataPort); +} + +#if SMC_DEBUG +void SMCReportDeviceStatus( unsigned long baseAddr ) +{ + unsigned long indexPort; + unsigned long dataPort; + unsigned char currentControl; + + indexPort = baseAddr; + dataPort = ( unsigned long )( ( char * )baseAddr + 1 ); + + outb(POWER_CONTROL, indexPort); + currentControl = inb(dataPort); + + if ( currentControl & ( 1 << FDC ) ) + printk( "\t+FDC Enabled\n" ); + else + printk( "\t-FDC Disabled\n" ); + + if ( currentControl & ( 1 << IDE1 ) ) + printk( "\t+IDE1 Enabled\n" ); + else + printk( "\t-IDE1 Disabled\n" ); + + if ( currentControl & ( 1 << IDE2 ) ) + printk( "\t+IDE2 Enabled\n" ); + else + printk( "\t-IDE2 Disabled\n" ); + + if ( currentControl & ( 1 << PARP ) ) + printk( "\t+PARP Enabled\n" ); + else + printk( "\t-PARP Disabled\n" ); + + if ( currentControl & ( 1 << SER1 ) ) + printk( "\t+SER1 Enabled\n" ); + else + printk( "\t-SER1 Disabled\n" ); + + if ( currentControl & ( 1 << SER2 ) ) + printk( "\t+SER2 Enabled\n" ); + else + printk( "\t-SER2 Disabled\n" ); + + printk( "\n" ); +} +#endif + +void SMC93X_Init(void) +{ + unsigned long SMCUltraBase; + + if ( ( SMCUltraBase = SMCDetectUltraIO( ) ) != ( unsigned long )0 ) { + printk( "SMC FDC37C93X Ultra I/O Controller found @ 0x%lx\n", + SMCUltraBase ); +#if SMC_DEBUG + SMCReportDeviceStatus( SMCUltraBase ); +#endif + SMCEnableDevice( SMCUltraBase, SER1, COM1_BASE, COM1_INTERRUPT ); + SMCEnableDevice( SMCUltraBase, SER2, COM2_BASE, COM2_INTERRUPT ); + SMCEnableDevice( SMCUltraBase, PARP, PARP_BASE, PARP_INTERRUPT ); + /* IDE on the SMC is not enabled; CMD646 (PCI) on MB */ + SMCEnableKYBD( SMCUltraBase ); + SMCEnableFDC( SMCUltraBase ); +#if SMC_DEBUG + SMCReportDeviceStatus( SMCUltraBase ); +#endif + SMCRunState( SMCUltraBase ); + } + else { +#if SMC_DEBUG + printk( "No SMC FDC37C93X Ultra I/O Controller found\n" ); +#endif + } +} + +#endif /* PC164 || LX164 */ + +/* we include MIATA because the GL has the SMC669 on board */ +#if defined(CONFIG_ALPHA_SX164) || defined(CONFIG_ALPHA_MIATA) + +#define SMC_DEBUG 0 + +/* File: smcc669_def.h + * + * Copyright (C) 1997 by + * Digital Equipment Corporation, Maynard, Massachusetts. + * All rights reserved. + * + * This software is furnished under a license and may be used and copied + * only in accordance of the terms of such license and with the + * inclusion of the above copyright notice. This software or any other + * copies thereof may not be provided or otherwise made available to any + * other person. No title to and ownership of the software is hereby + * transferred. + * + * The information in this software is subject to change without notice + * and should not be construed as a commitment by Digital Equipment + * Corporation. + * + * Digital assumes no responsibility for the use or reliability of its + * software on equipment which is not supplied by Digital. + * + * + * Abstract: + * + * This file contains header definitions for the SMC37c669 + * Super I/O controller. + * + * Author: + * + * Eric Rasmussen + * + * Modification History: + * + * er 28-Jan-1997 Initial Entry + */ + +#ifndef __SMC37c669_H +#define __SMC37c669_H + +/* +** Macros for handling device IRQs +** +** The mask acts as a flag used in mapping actual ISA IRQs (0 - 15) +** to device IRQs (A - H). +*/ +#define SMC37c669_DEVICE_IRQ_MASK 0x80000000 +#define SMC37c669_DEVICE_IRQ( __i ) \ + ((SMC37c669_DEVICE_IRQ_MASK) | (__i)) +#define SMC37c669_IS_DEVICE_IRQ(__i) \ + (((__i) & (SMC37c669_DEVICE_IRQ_MASK)) == (SMC37c669_DEVICE_IRQ_MASK)) +#define SMC37c669_RAW_DEVICE_IRQ(__i) \ + ((__i) & ~(SMC37c669_DEVICE_IRQ_MASK)) + +/* +** Macros for handling device DRQs +** +** The mask acts as a flag used in mapping actual ISA DMA +** channels to device DMA channels (A - C). +*/ +#define SMC37c669_DEVICE_DRQ_MASK 0x80000000 +#define SMC37c669_DEVICE_DRQ(__d) \ + ((SMC37c669_DEVICE_DRQ_MASK) | (__d)) +#define SMC37c669_IS_DEVICE_DRQ(__d) \ + (((__d) & (SMC37c669_DEVICE_DRQ_MASK)) == (SMC37c669_DEVICE_DRQ_MASK)) +#define SMC37c669_RAW_DEVICE_DRQ(__d) \ + ((__d) & ~(SMC37c669_DEVICE_DRQ_MASK)) + +#define SMC37c669_DEVICE_ID 0x3 + +/* +** SMC37c669 Device Function Definitions +*/ +#define SERIAL_0 0 +#define SERIAL_1 1 +#define PARALLEL_0 2 +#define FLOPPY_0 3 +#define IDE_0 4 +#define NUM_FUNCS 5 + +/* +** Default Device Function Mappings +*/ +#define COM1_BASE 0x3F8 +#define COM1_IRQ 4 +#define COM2_BASE 0x2F8 +#define COM2_IRQ 3 +#define PARP_BASE 0x3BC +#define PARP_IRQ 7 +#define PARP_DRQ 3 +#define FDC_BASE 0x3F0 +#define FDC_IRQ 6 +#define FDC_DRQ 2 + +/* +** Configuration On/Off Key Definitions +*/ +#define SMC37c669_CONFIG_ON_KEY 0x55 +#define SMC37c669_CONFIG_OFF_KEY 0xAA + +/* +** SMC 37c669 Device IRQs +*/ +#define SMC37c669_DEVICE_IRQ_A ( SMC37c669_DEVICE_IRQ( 0x01 ) ) +#define SMC37c669_DEVICE_IRQ_B ( SMC37c669_DEVICE_IRQ( 0x02 ) ) +#define SMC37c669_DEVICE_IRQ_C ( SMC37c669_DEVICE_IRQ( 0x03 ) ) +#define SMC37c669_DEVICE_IRQ_D ( SMC37c669_DEVICE_IRQ( 0x04 ) ) +#define SMC37c669_DEVICE_IRQ_E ( SMC37c669_DEVICE_IRQ( 0x05 ) ) +#define SMC37c669_DEVICE_IRQ_F ( SMC37c669_DEVICE_IRQ( 0x06 ) ) +/* SMC37c669_DEVICE_IRQ_G *** RESERVED ***/ +#define SMC37c669_DEVICE_IRQ_H ( SMC37c669_DEVICE_IRQ( 0x08 ) ) + +/* +** SMC 37c669 Device DMA Channel Definitions +*/ +#define SMC37c669_DEVICE_DRQ_A ( SMC37c669_DEVICE_DRQ( 0x01 ) ) +#define SMC37c669_DEVICE_DRQ_B ( SMC37c669_DEVICE_DRQ( 0x02 ) ) +#define SMC37c669_DEVICE_DRQ_C ( SMC37c669_DEVICE_DRQ( 0x03 ) ) + +/* +** Configuration Register Index Definitions +*/ +#define SMC37c669_CR00_INDEX 0x00 +#define SMC37c669_CR01_INDEX 0x01 +#define SMC37c669_CR02_INDEX 0x02 +#define SMC37c669_CR03_INDEX 0x03 +#define SMC37c669_CR04_INDEX 0x04 +#define SMC37c669_CR05_INDEX 0x05 +#define SMC37c669_CR06_INDEX 0x06 +#define SMC37c669_CR07_INDEX 0x07 +#define SMC37c669_CR08_INDEX 0x08 +#define SMC37c669_CR09_INDEX 0x09 +#define SMC37c669_CR0A_INDEX 0x0A +#define SMC37c669_CR0B_INDEX 0x0B +#define SMC37c669_CR0C_INDEX 0x0C +#define SMC37c669_CR0D_INDEX 0x0D +#define SMC37c669_CR0E_INDEX 0x0E +#define SMC37c669_CR0F_INDEX 0x0F +#define SMC37c669_CR10_INDEX 0x10 +#define SMC37c669_CR11_INDEX 0x11 +#define SMC37c669_CR12_INDEX 0x12 +#define SMC37c669_CR13_INDEX 0x13 +#define SMC37c669_CR14_INDEX 0x14 +#define SMC37c669_CR15_INDEX 0x15 +#define SMC37c669_CR16_INDEX 0x16 +#define SMC37c669_CR17_INDEX 0x17 +#define SMC37c669_CR18_INDEX 0x18 +#define SMC37c669_CR19_INDEX 0x19 +#define SMC37c669_CR1A_INDEX 0x1A +#define SMC37c669_CR1B_INDEX 0x1B +#define SMC37c669_CR1C_INDEX 0x1C +#define SMC37c669_CR1D_INDEX 0x1D +#define SMC37c669_CR1E_INDEX 0x1E +#define SMC37c669_CR1F_INDEX 0x1F +#define SMC37c669_CR20_INDEX 0x20 +#define SMC37c669_CR21_INDEX 0x21 +#define SMC37c669_CR22_INDEX 0x22 +#define SMC37c669_CR23_INDEX 0x23 +#define SMC37c669_CR24_INDEX 0x24 +#define SMC37c669_CR25_INDEX 0x25 +#define SMC37c669_CR26_INDEX 0x26 +#define SMC37c669_CR27_INDEX 0x27 +#define SMC37c669_CR28_INDEX 0x28 +#define SMC37c669_CR29_INDEX 0x29 + +/* +** Configuration Register Alias Definitions +*/ +#define SMC37c669_DEVICE_ID_INDEX SMC37c669_CR0D_INDEX +#define SMC37c669_DEVICE_REVISION_INDEX SMC37c669_CR0E_INDEX +#define SMC37c669_FDC_BASE_ADDRESS_INDEX SMC37c669_CR20_INDEX +#define SMC37c669_IDE_BASE_ADDRESS_INDEX SMC37c669_CR21_INDEX +#define SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX SMC37c669_CR22_INDEX +#define SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX SMC37c669_CR23_INDEX +#define SMC37c669_SERIAL0_BASE_ADDRESS_INDEX SMC37c669_CR24_INDEX +#define SMC37c669_SERIAL1_BASE_ADDRESS_INDEX SMC37c669_CR25_INDEX +#define SMC37c669_PARALLEL_FDC_DRQ_INDEX SMC37c669_CR26_INDEX +#define SMC37c669_PARALLEL_FDC_IRQ_INDEX SMC37c669_CR27_INDEX +#define SMC37c669_SERIAL_IRQ_INDEX SMC37c669_CR28_INDEX + +/* +** Configuration Register Definitions +** +** The INDEX (write only) and DATA (read/write) ports are effective +** only when the chip is in the Configuration State. +*/ +typedef struct _SMC37c669_CONFIG_REGS { + unsigned char index_port; + unsigned char data_port; +} SMC37c669_CONFIG_REGS; + +/* +** CR00 - default value 0x28 +** +** IDE_EN (CR00<1:0>): +** 0x - 30ua pull-ups on nIDEEN, nHDCS0, NHDCS1 +** 11 - IRQ_H available as IRQ output, +** IRRX2, IRTX2 available as alternate IR pins +** 10 - nIDEEN, nHDCS0, nHDCS1 used to control IDE +** +** VALID (CR00<7>): +** A high level on this software controlled bit can +** be used to indicate that a valid configuration +** cycle has occurred. The control software must +** take care to set this bit at the appropriate times. +** Set to zero after power up. This bit has no +** effect on any other hardware in the chip. +** +*/ +typedef union _SMC37c669_CR00 { + unsigned char as_uchar; + struct { + unsigned ide_en : 2; /* See note above */ + unsigned reserved1 : 1; /* RAZ */ + unsigned fdc_pwr : 1; /* 1 = supply power to FDC */ + unsigned reserved2 : 3; /* Read as 010b */ + unsigned valid : 1; /* See note above */ + } by_field; +} SMC37c669_CR00; + +/* +** CR01 - default value 0x9C +*/ +typedef union _SMC37c669_CR01 { + unsigned char as_uchar; + struct { + unsigned reserved1 : 2; /* RAZ */ + unsigned ppt_pwr : 1; /* 1 = supply power to PPT */ + unsigned ppt_mode : 1; /* 1 = Printer mode, 0 = EPP */ + unsigned reserved2 : 1; /* Read as 1 */ + unsigned reserved3 : 2; /* RAZ */ + unsigned lock_crx: 1; /* Lock CR00 - CR18 */ + } by_field; +} SMC37c669_CR01; + +/* +** CR02 - default value 0x88 +*/ +typedef union _SMC37c669_CR02 { + unsigned char as_uchar; + struct { + unsigned reserved1 : 3; /* RAZ */ + unsigned uart1_pwr : 1; /* 1 = supply power to UART1 */ + unsigned reserved2 : 3; /* RAZ */ + unsigned uart2_pwr : 1; /* 1 = supply power to UART2 */ + } by_field; +} SMC37c669_CR02; + +/* +** CR03 - default value 0x78 +** +** CR03<7> CR03<2> Pin 94 +** ------- ------- ------ +** 0 X DRV2 (input) +** 1 0 ADRX +** 1 1 IRQ_B +** +** CR03<6> CR03<5> Op Mode +** ------- ------- ------- +** 0 0 Model 30 +** 0 1 PS/2 +** 1 0 Reserved +** 1 1 AT Mode +*/ +typedef union _SMC37c669_CR03 { + unsigned char as_uchar; + struct { + unsigned pwrgd_gamecs : 1; /* 1 = PWRGD, 0 = GAMECS */ + unsigned fdc_mode2 : 1; /* 1 = Enhanced Mode 2 */ + unsigned pin94_0 : 1; /* See note above */ + unsigned reserved1 : 1; /* RAZ */ + unsigned drvden : 1; /* 1 = high, 0 - output */ + unsigned op_mode : 2; /* See note above */ + unsigned pin94_1 : 1; /* See note above */ + } by_field; +} SMC37c669_CR03; + +/* +** CR04 - default value 0x00 +** +** PP_EXT_MODE: +** If CR01 = 0 and PP_EXT_MODE = +** 00 - Standard and Bidirectional +** 01 - EPP mode and SPP +** 10 - ECP mode +** In this mode, 2 drives can be supported +** directly, 3 or 4 drives must use external +** 4 drive support. SPP can be selected +** through the ECR register of ECP as mode 000. +** 11 - ECP mode and EPP mode +** In this mode, 2 drives can be supported +** directly, 3 or 4 drives must use external +** 4 drive support. SPP can be selected +** through the ECR register of ECP as mode 000. +** In this mode, EPP can be selected through +** the ECR register of ECP as mode 100. +** +** PP_FDC: +** 00 - Normal +** 01 - PPFD1 +** 10 - PPFD2 +** 11 - Reserved +** +** MIDI1: +** Serial Clock Select: +** A low level on this bit disables MIDI support, +** clock = divide by 13. A high level on this +** bit enables MIDI support, clock = divide by 12. +** +** MIDI operates at 31.25 Kbps which can be derived +** from 125 KHz (24 MHz / 12 = 2 MHz, 2 MHz / 16 = 125 KHz) +** +** ALT_IO: +** 0 - Use pins IRRX, IRTX +** 1 - Use pins IRRX2, IRTX2 +** +** If this bit is set, the IR receive and transmit +** functions will not be available on pins 25 and 26 +** unless CR00 = 11. +*/ +typedef union _SMC37c669_CR04 { + unsigned char as_uchar; + struct { + unsigned ppt_ext_mode : 2; /* See note above */ + unsigned ppt_fdc : 2; /* See note above */ + unsigned midi1 : 1; /* See note above */ + unsigned midi2 : 1; /* See note above */ + unsigned epp_type : 1; /* 0 = EPP 1.9, 1 = EPP 1.7 */ + unsigned alt_io : 1; /* See note above */ + } by_field; +} SMC37c669_CR04; + +/* +** CR05 - default value 0x00 +** +** DEN_SEL: +** 00 - Densel output normal +** 01 - Reserved +** 10 - Densel output 1 +** 11 - Densel output 0 +** +*/ +typedef union _SMC37c669_CR05 { + unsigned char as_uchar; + struct { + unsigned reserved1 : 2; /* RAZ */ + unsigned fdc_dma_mode : 1; /* 0 = burst, 1 = non-burst */ + unsigned den_sel : 2; /* See note above */ + unsigned swap_drv : 1; /* Swap the FDC motor selects */ + unsigned extx4 : 1; /* 0 = 2 drive, 1 = external 4 drive decode */ + unsigned reserved2 : 1; /* RAZ */ + } by_field; +} SMC37c669_CR05; + +/* +** CR06 - default value 0xFF +*/ +typedef union _SMC37c669_CR06 { + unsigned char as_uchar; + struct { + unsigned floppy_a : 2; /* Type of floppy drive A */ + unsigned floppy_b : 2; /* Type of floppy drive B */ + unsigned floppy_c : 2; /* Type of floppy drive C */ + unsigned floppy_d : 2; /* Type of floppy drive D */ + } by_field; +} SMC37c669_CR06; + +/* +** CR07 - default value 0x00 +** +** Auto Power Management CR07<7:4>: +** 0 - Auto Powerdown disabled (default) +** 1 - Auto Powerdown enabled +** +** This bit is reset to the default state by POR or +** a hardware reset. +** +*/ +typedef union _SMC37c669_CR07 { + unsigned char as_uchar; + struct { + unsigned floppy_boot : 2; /* 0 = A:, 1 = B: */ + unsigned reserved1 : 2; /* RAZ */ + unsigned ppt_en : 1; /* See note above */ + unsigned uart1_en : 1; /* See note above */ + unsigned uart2_en : 1; /* See note above */ + unsigned fdc_en : 1; /* See note above */ + } by_field; +} SMC37c669_CR07; + +/* +** CR08 - default value 0x00 +*/ +typedef union _SMC37c669_CR08 { + unsigned char as_uchar; + struct { + unsigned zero : 4; /* 0 */ + unsigned addrx7_4 : 4; /* ADR<7:3> for ADRx decode */ + } by_field; +} SMC37c669_CR08; + +/* +** CR09 - default value 0x00 +** +** ADRx_CONFIG: +** 00 - ADRx disabled +** 01 - 1 byte decode A<3:0> = 0000b +** 10 - 8 byte block decode A<3:0> = 0XXXb +** 11 - 16 byte block decode A<3:0> = XXXXb +** +*/ +typedef union _SMC37c669_CR09 { + unsigned char as_uchar; + struct { + unsigned adra8 : 3; /* ADR<10:8> for ADRx decode */ + unsigned reserved1 : 3; + unsigned adrx_config : 2; /* See note above */ + } by_field; +} SMC37c669_CR09; + +/* +** CR0A - default value 0x00 +*/ +typedef union _SMC37c669_CR0A { + unsigned char as_uchar; + struct { + unsigned ecp_fifo_threshold : 4; + unsigned reserved1 : 4; + } by_field; +} SMC37c669_CR0A; + +/* +** CR0B - default value 0x00 +*/ +typedef union _SMC37c669_CR0B { + unsigned char as_uchar; + struct { + unsigned fdd0_drtx : 2; /* FDD0 Data Rate Table */ + unsigned fdd1_drtx : 2; /* FDD1 Data Rate Table */ + unsigned fdd2_drtx : 2; /* FDD2 Data Rate Table */ + unsigned fdd3_drtx : 2; /* FDD3 Data Rate Table */ + } by_field; +} SMC37c669_CR0B; + +/* +** CR0C - default value 0x00 +** +** UART2_MODE: +** 000 - Standard (default) +** 001 - IrDA (HPSIR) +** 010 - Amplitude Shift Keyed IR @500 KHz +** 011 - Reserved +** 1xx - Reserved +** +*/ +typedef union _SMC37c669_CR0C { + unsigned char as_uchar; + struct { + unsigned uart2_rcv_polarity : 1; /* 1 = invert RX */ + unsigned uart2_xmit_polarity : 1; /* 1 = invert TX */ + unsigned uart2_duplex : 1; /* 1 = full, 0 = half */ + unsigned uart2_mode : 3; /* See note above */ + unsigned uart1_speed : 1; /* 1 = high speed enabled */ + unsigned uart2_speed : 1; /* 1 = high speed enabled */ + } by_field; +} SMC37c669_CR0C; + +/* +** CR0D - default value 0x03 +** +** Device ID Register - read only +*/ +typedef union _SMC37c669_CR0D { + unsigned char as_uchar; + struct { + unsigned device_id : 8; /* Returns 0x3 in this field */ + } by_field; +} SMC37c669_CR0D; + +/* +** CR0E - default value 0x02 +** +** Device Revision Register - read only +*/ +typedef union _SMC37c669_CR0E { + unsigned char as_uchar; + struct { + unsigned device_rev : 8; /* Returns 0x2 in this field */ + } by_field; +} SMC37c669_CR0E; + +/* +** CR0F - default value 0x00 +*/ +typedef union _SMC37c669_CR0F { + unsigned char as_uchar; + struct { + unsigned test0 : 1; /* Reserved - set to 0 */ + unsigned test1 : 1; /* Reserved - set to 0 */ + unsigned test2 : 1; /* Reserved - set to 0 */ + unsigned test3 : 1; /* Reserved - set t0 0 */ + unsigned test4 : 1; /* Reserved - set to 0 */ + unsigned test5 : 1; /* Reserved - set t0 0 */ + unsigned test6 : 1; /* Reserved - set t0 0 */ + unsigned test7 : 1; /* Reserved - set to 0 */ + } by_field; +} SMC37c669_CR0F; + +/* +** CR10 - default value 0x00 +*/ +typedef union _SMC37c669_CR10 { + unsigned char as_uchar; + struct { + unsigned reserved1 : 3; /* RAZ */ + unsigned pll_gain : 1; /* 1 = 3V, 2 = 5V operation */ + unsigned pll_stop : 1; /* 1 = stop PLLs */ + unsigned ace_stop : 1; /* 1 = stop UART clocks */ + unsigned pll_clock_ctrl : 1; /* 0 = 14.318 MHz, 1 = 24 MHz */ + unsigned ir_test : 1; /* Enable IR test mode */ + } by_field; +} SMC37c669_CR10; + +/* +** CR11 - default value 0x00 +*/ +typedef union _SMC37c669_CR11 { + unsigned char as_uchar; + struct { + unsigned ir_loopback : 1; /* Internal IR loop back */ + unsigned test_10ms : 1; /* Test 10ms autopowerdown FDC timeout */ + unsigned reserved1 : 6; /* RAZ */ + } by_field; +} SMC37c669_CR11; + +/* +** CR12 - CR1D are reserved registers +*/ + +/* +** CR1E - default value 0x80 +** +** GAMECS: +** 00 - GAMECS disabled +** 01 - 1 byte decode ADR<3:0> = 0001b +** 10 - 8 byte block decode ADR<3:0> = 0XXXb +** 11 - 16 byte block decode ADR<3:0> = XXXXb +** +*/ +typedef union _SMC37c66_CR1E { + unsigned char as_uchar; + struct { + unsigned gamecs_config: 2; /* See note above */ + unsigned gamecs_addr9_4 : 6; /* GAMECS Addr<9:4> */ + } by_field; +} SMC37c669_CR1E; + +/* +** CR1F - default value 0x00 +** +** DT0 DT1 DRVDEN0 DRVDEN1 Drive Type +** --- --- ------- ------- ---------- +** 0 0 DENSEL DRATE0 4/2/1 MB 3.5" +** 2/1 MB 5.25" +** 2/1.6/1 MB 3.5" (3-mode) +** 0 1 DRATE1 DRATE0 +** 1 0 nDENSEL DRATE0 PS/2 +** 1 1 DRATE0 DRATE1 +** +** Note: DENSEL, DRATE1, and DRATE0 map onto two output +** pins - DRVDEN0 and DRVDEN1. +** +*/ +typedef union _SMC37c669_CR1F { + unsigned char as_uchar; + struct { + unsigned fdd0_drive_type : 2; /* FDD0 drive type */ + unsigned fdd1_drive_type : 2; /* FDD1 drive type */ + unsigned fdd2_drive_type : 2; /* FDD2 drive type */ + unsigned fdd3_drive_type : 2; /* FDD3 drive type */ + } by_field; +} SMC37c669_CR1F; + +/* +** CR20 - default value 0x3C +** +** FDC Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0, A<3:0> = 0XXXb to access. +** +*/ +typedef union _SMC37c669_CR20 { + unsigned char as_uchar; + struct { + unsigned zero : 2; /* 0 */ + unsigned addr9_4 : 6; /* FDC Addr<9:4> */ + } by_field; +} SMC37c669_CR20; + +/* +** CR21 - default value 0x3C +** +** IDE Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0, A<3:0> = 0XXXb to access. +** +*/ +typedef union _SMC37c669_CR21 { + unsigned char as_uchar; + struct { + unsigned zero : 2; /* 0 */ + unsigned addr9_4 : 6; /* IDE Addr<9:4> */ + } by_field; +} SMC37c669_CR21; + +/* +** CR22 - default value 0x3D +** +** IDE Alternate Status Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0, A<3:0> = 0110b to access. +** +*/ +typedef union _SMC37c669_CR22 { + unsigned char as_uchar; + struct { + unsigned zero : 2; /* 0 */ + unsigned addr9_4 : 6; /* IDE Alt Status Addr<9:4> */ + } by_field; +} SMC37c669_CR22; + +/* +** CR23 - default value 0x00 +** +** Parallel Port Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0 to access. +** - If EPP is enabled, A<2:0> = XXXb to access. +** If EPP is NOT enabled, A<1:0> = XXb to access +** +*/ +typedef union _SMC37c669_CR23 { + unsigned char as_uchar; + struct { + unsigned addr9_2 : 8; /* Parallel Port Addr<9:2> */ + } by_field; +} SMC37c669_CR23; + +/* +** CR24 - default value 0x00 +** +** UART1 Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0, A<2:0> = XXXb to access. +** +*/ +typedef union _SMC37c669_CR24 { + unsigned char as_uchar; + struct { + unsigned zero : 1; /* 0 */ + unsigned addr9_3 : 7; /* UART1 Addr<9:3> */ + } by_field; +} SMC37c669_CR24; + +/* +** CR25 - default value 0x00 +** +** UART2 Base Address Register +** - To disable this decode set Addr<9:8> = 0 +** - A<10> = 0, A<2:0> = XXXb to access. +** +*/ +typedef union _SMC37c669_CR25 { + unsigned char as_uchar; + struct { + unsigned zero : 1; /* 0 */ + unsigned addr9_3 : 7; /* UART2 Addr<9:3> */ + } by_field; +} SMC37c669_CR25; + +/* +** CR26 - default value 0x00 +** +** Parallel Port / FDC DMA Select Register +** +** D3 - D0 DMA +** D7 - D4 Selected +** ------- -------- +** 0000 None +** 0001 DMA_A +** 0010 DMA_B +** 0011 DMA_C +** +*/ +typedef union _SMC37c669_CR26 { + unsigned char as_uchar; + struct { + unsigned ppt_drq : 4; /* See note above */ + unsigned fdc_drq : 4; /* See note above */ + } by_field; +} SMC37c669_CR26; + +/* +** CR27 - default value 0x00 +** +** Parallel Port / FDC IRQ Select Register +** +** D3 - D0 IRQ +** D7 - D4 Selected +** ------- -------- +** 0000 None +** 0001 IRQ_A +** 0010 IRQ_B +** 0011 IRQ_C +** 0100 IRQ_D +** 0101 IRQ_E +** 0110 IRQ_F +** 0111 Reserved +** 1000 IRQ_H +** +** Any unselected IRQ REQ is in tristate +** +*/ +typedef union _SMC37c669_CR27 { + unsigned char as_uchar; + struct { + unsigned ppt_irq : 4; /* See note above */ + unsigned fdc_irq : 4; /* See note above */ + } by_field; +} SMC37c669_CR27; + +/* +** CR28 - default value 0x00 +** +** UART IRQ Select Register +** +** D3 - D0 IRQ +** D7 - D4 Selected +** ------- -------- +** 0000 None +** 0001 IRQ_A +** 0010 IRQ_B +** 0011 IRQ_C +** 0100 IRQ_D +** 0101 IRQ_E +** 0110 IRQ_F +** 0111 Reserved +** 1000 IRQ_H +** 1111 share with UART1 (only for UART2) +** +** Any unselected IRQ REQ is in tristate +** +** To share an IRQ between UART1 and UART2, set +** UART1 to use the desired IRQ and set UART2 to +** 0xF to enable sharing mechanism. +** +*/ +typedef union _SMC37c669_CR28 { + unsigned char as_uchar; + struct { + unsigned uart2_irq : 4; /* See note above */ + unsigned uart1_irq : 4; /* See note above */ + } by_field; +} SMC37c669_CR28; + +/* +** CR29 - default value 0x00 +** +** IRQIN IRQ Select Register +** +** D3 - D0 IRQ +** D7 - D4 Selected +** ------- -------- +** 0000 None +** 0001 IRQ_A +** 0010 IRQ_B +** 0011 IRQ_C +** 0100 IRQ_D +** 0101 IRQ_E +** 0110 IRQ_F +** 0111 Reserved +** 1000 IRQ_H +** +** Any unselected IRQ REQ is in tristate +** +*/ +typedef union _SMC37c669_CR29 { + unsigned char as_uchar; + struct { + unsigned irqin_irq : 4; /* See note above */ + unsigned reserved1 : 4; /* RAZ */ + } by_field; +} SMC37c669_CR29; + +/* +** Aliases of Configuration Register formats (should match +** the set of index aliases). +** +** Note that CR24 and CR25 have the same format and are the +** base address registers for UART1 and UART2. Because of +** this we only define 1 alias here - for CR24 - as the serial +** base address register. +** +** Note that CR21 and CR22 have the same format and are the +** base address and alternate status address registers for +** the IDE controller. Because of this we only define 1 alias +** here - for CR21 - as the IDE address register. +** +*/ +typedef SMC37c669_CR0D SMC37c669_DEVICE_ID_REGISTER; +typedef SMC37c669_CR0E SMC37c669_DEVICE_REVISION_REGISTER; +typedef SMC37c669_CR20 SMC37c669_FDC_BASE_ADDRESS_REGISTER; +typedef SMC37c669_CR21 SMC37c669_IDE_ADDRESS_REGISTER; +typedef SMC37c669_CR23 SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER; +typedef SMC37c669_CR24 SMC37c669_SERIAL_BASE_ADDRESS_REGISTER; +typedef SMC37c669_CR26 SMC37c669_PARALLEL_FDC_DRQ_REGISTER; +typedef SMC37c669_CR27 SMC37c669_PARALLEL_FDC_IRQ_REGISTER; +typedef SMC37c669_CR28 SMC37c669_SERIAL_IRQ_REGISTER; + +/* +** ISA/Device IRQ Translation Table Entry Definition +*/ +typedef struct _SMC37c669_IRQ_TRANSLATION_ENTRY { + int device_irq; + int isa_irq; +} SMC37c669_IRQ_TRANSLATION_ENTRY; + +/* +** ISA/Device DMA Translation Table Entry Definition +*/ +typedef struct _SMC37c669_DRQ_TRANSLATION_ENTRY { + int device_drq; + int isa_drq; +} SMC37c669_DRQ_TRANSLATION_ENTRY; + +/* +** External Interface Function Prototype Declarations +*/ + +SMC37c669_CONFIG_REGS *SMC37c669_detect( + void +); + +unsigned int SMC37c669_enable_device( + unsigned int func +); + +unsigned int SMC37c669_disable_device( + unsigned int func +); + +unsigned int SMC37c669_configure_device( + unsigned int func, + int port, + int irq, + int drq +); + +void SMC37c669_display_device_info( + void +); + +#endif /* __SMC37c669_H */ + +/* file: smcc669.c + * + * Copyright (C) 1997 by + * Digital Equipment Corporation, Maynard, Massachusetts. + * All rights reserved. + * + * This software is furnished under a license and may be used and copied + * only in accordance of the terms of such license and with the + * inclusion of the above copyright notice. This software or any other + * copies thereof may not be provided or otherwise made available to any + * other person. No title to and ownership of the software is hereby + * transferred. + * + * The information in this software is subject to change without notice + * and should not be construed as a commitment by digital equipment + * corporation. + * + * Digital assumes no responsibility for the use or reliability of its + * software on equipment which is not supplied by digital. + */ + +/* + *++ + * FACILITY: + * + * Alpha SRM Console Firmware + * + * MODULE DESCRIPTION: + * + * SMC37c669 Super I/O controller configuration routines. + * + * AUTHORS: + * + * Eric Rasmussen + * + * CREATION DATE: + * + * 28-Jan-1997 + * + * MODIFICATION HISTORY: + * + * er 01-May-1997 Fixed pointer conversion errors in + * SMC37c669_get_device_config(). + * er 28-Jan-1997 Initial version. + * + *-- + */ +#if 0 +/* $INCLUDE_OPTIONS$ */ +#include "cp$inc:platform_io.h" +/* $INCLUDE_OPTIONS_END$ */ +#include "cp$src:common.h" +#include "cp$inc:prototypes.h" +#include "cp$src:kernel_def.h" +#include "cp$src:msg_def.h" +#include "cp$src:smcc669_def.h" +/* Platform-specific includes */ +#include "cp$src:platform.h" +#endif + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#define wb( _x_, _y_ ) outb( _y_, (unsigned int)((unsigned long)_x_) ) +#define rb( _x_ ) inb( (unsigned int)((unsigned long)_x_) ) + +/* +** Local storage for device configuration information. +** +** Since the SMC37c669 does not provide an explicit +** mechanism for enabling/disabling individual device +** functions, other than unmapping the device, local +** storage for device configuration information is +** allocated here for use in implementing our own +** function enable/disable scheme. +*/ +static struct DEVICE_CONFIG { + unsigned int port1; + unsigned int port2; + unsigned int irq; + unsigned int drq; +} local_config [NUM_FUNCS]; + +/* +** List of all possible addresses for the Super I/O chip +*/ +static unsigned long SMC37c669_Addresses[] = + { + 0x3F0UL, /* Primary address */ + 0x370UL, /* Secondary address */ + 0UL /* End of list */ + }; + +/* +** Global Pointer to the Super I/O device +*/ +static SMC37c669_CONFIG_REGS *SMC37c669 = NULL; + +/* +** IRQ Translation Table +** +** The IRQ translation table is a list of SMC37c669 device +** and standard ISA IRQs. +** +*/ +static SMC37c669_IRQ_TRANSLATION_ENTRY *SMC37c669_irq_table; + +/* +** The following definition is for the default IRQ +** translation table. +*/ +static SMC37c669_IRQ_TRANSLATION_ENTRY SMC37c669_default_irq_table[ ] = + { + { SMC37c669_DEVICE_IRQ_A, -1 }, + { SMC37c669_DEVICE_IRQ_B, -1 }, + { SMC37c669_DEVICE_IRQ_C, 7 }, + { SMC37c669_DEVICE_IRQ_D, 6 }, + { SMC37c669_DEVICE_IRQ_E, 4 }, + { SMC37c669_DEVICE_IRQ_F, 3 }, + { SMC37c669_DEVICE_IRQ_H, -1 }, + { -1, -1 } /* End of table */ + }; + +/* +** DRQ Translation Table +** +** The DRQ translation table is a list of SMC37c669 device and +** ISA DMA channels. +** +*/ +static SMC37c669_DRQ_TRANSLATION_ENTRY *SMC37c669_drq_table; + +/* +** The following definition is the default DRQ +** translation table. +*/ +static SMC37c669_DRQ_TRANSLATION_ENTRY SMC37c669_default_drq_table[ ] = + { + { SMC37c669_DEVICE_DRQ_A, 2 }, + { SMC37c669_DEVICE_DRQ_B, 3 }, + { SMC37c669_DEVICE_DRQ_C, -1 }, + { -1, -1 } /* End of table */ + }; + +/* +** Local Function Prototype Declarations +*/ + +static unsigned int SMC37c669_is_device_enabled( + unsigned int func +); + +#if 0 +static unsigned int SMC37c669_get_device_config( + unsigned int func, + int *port, + int *irq, + int *drq +); +#endif + +static void SMC37c669_config_mode( + unsigned int enable +); + +static unsigned char SMC37c669_read_config( + unsigned char index +); + +static void SMC37c669_write_config( + unsigned char index, + unsigned char data +); + +static void SMC37c669_init_local_config( void ); + +static struct DEVICE_CONFIG *SMC37c669_get_config( + unsigned int func +); + +static int SMC37c669_xlate_irq( + unsigned int irq +); + +static int SMC37c669_xlate_drq( + unsigned int drq +); + +#if 0 +/* +** External Data Declarations +*/ + +extern struct LOCK spl_atomic; + +/* +** External Function Prototype Declarations +*/ + +/* From kernel_alpha.mar */ +extern spinlock( + struct LOCK *spl +); + +extern spinunlock( + struct LOCK *spl +); + +/* From filesys.c */ +int allocinode( + char *name, + int can_create, + struct INODE **ipp +); + +extern int null_procedure( void ); + +int smcc669_init( void ); +int smcc669_open( struct FILE *fp, char *info, char *next, char *mode ); +int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf ); +int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf ); +int smcc669_close( struct FILE *fp ); + +struct DDB smc_ddb = { + "smc", /* how this routine wants to be called */ + smcc669_read, /* read routine */ + smcc669_write, /* write routine */ + smcc669_open, /* open routine */ + smcc669_close, /* close routine */ + null_procedure, /* name expansion routine */ + null_procedure, /* delete routine */ + null_procedure, /* create routine */ + null_procedure, /* setmode */ + null_procedure, /* validation routine */ + 0, /* class specific use */ + 1, /* allows information */ + 0, /* must be stacked */ + 0, /* is a flash update driver */ + 0, /* is a block device */ + 0, /* not seekable */ + 0, /* is an ethernet device */ + 0, /* is a filesystem driver */ +}; +#endif + +#define spinlock(x) +#define spinunlock(x) + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function detects the presence of an SMC37c669 Super I/O +** controller. +** +** FORMAL PARAMETERS: +** +** None +** +** RETURN VALUE: +** +** Returns a pointer to the device if found, otherwise, +** the NULL pointer is returned. +** +** SIDE EFFECTS: +** +** None +** +**-- +*/ +SMC37c669_CONFIG_REGS *SMC37c669_detect( void ) +{ + int i; + SMC37c669_DEVICE_ID_REGISTER id; + + for ( i = 0; SMC37c669_Addresses[i] != 0; i++ ) { +/* +** Initialize the device pointer even though we don't yet know if +** the controller is at this address. The support functions access +** the controller through this device pointer so we need to set it +** even when we are looking ... +*/ + SMC37c669 = ( SMC37c669_CONFIG_REGS * )SMC37c669_Addresses[i]; +/* +** Enter configuration mode +*/ + SMC37c669_config_mode( TRUE ); +/* +** Read the device id +*/ + id.as_uchar = SMC37c669_read_config( SMC37c669_DEVICE_ID_INDEX ); +/* +** Exit configuration mode +*/ + SMC37c669_config_mode( FALSE ); +/* +** Does the device id match? If so, assume we have found an +** SMC37c669 controller at this address. +*/ + if ( id.by_field.device_id == SMC37c669_DEVICE_ID ) { +/* +** Initialize the IRQ and DRQ translation tables. +*/ + SMC37c669_irq_table = SMC37c669_default_irq_table; + SMC37c669_drq_table = SMC37c669_default_drq_table; +/* +** erfix +** +** If the platform can't use the IRQ and DRQ defaults set up in this +** file, it should call a platform-specific external routine at this +** point to reset the IRQ and DRQ translation table pointers to point +** at the appropriate tables for the platform. If the defaults are +** acceptable, then the external routine should do nothing. +*/ + +/* +** Put the chip back into configuration mode +*/ + SMC37c669_config_mode( TRUE ); +/* +** Initialize local storage for configuration information +*/ + SMC37c669_init_local_config( ); +/* +** Exit configuration mode +*/ + SMC37c669_config_mode( FALSE ); +/* +** SMC37c669 controller found, break out of search loop +*/ + break; + } + else { +/* +** Otherwise, we did not find an SMC37c669 controller at this +** address so set the device pointer to NULL. +*/ + SMC37c669 = NULL; + } + } + return SMC37c669; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function enables an SMC37c669 device function. +** +** FORMAL PARAMETERS: +** +** func: +** Which device function to enable +** +** RETURN VALUE: +** +** Returns TRUE is the device function was enabled, otherwise, FALSE +** +** SIDE EFFECTS: +** +** {@description or none@} +** +** DESIGN: +** +** Enabling a device function in the SMC37c669 controller involves +** setting all of its mappings (port, irq, drq ...). A local +** "shadow" copy of the device configuration is kept so we can +** just set each mapping to what the local copy says. +** +** This function ALWAYS updates the local shadow configuration of +** the device function being enabled, even if the device is always +** enabled. To avoid replication of code, functions such as +** configure_device set up the local copy and then call this +** function to the update the real device. +** +**-- +*/ +unsigned int SMC37c669_enable_device ( unsigned int func ) +{ + unsigned int ret_val = FALSE; +/* +** Put the device into configuration mode +*/ + SMC37c669_config_mode( TRUE ); + switch ( func ) { + case SERIAL_0: + { + SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_SERIAL_IRQ_REGISTER irq; +/* +** Enable the serial 1 IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); + + irq.by_field.uart1_irq = + SMC37c669_RAW_DEVICE_IRQ( + SMC37c669_xlate_irq( local_config[ func ].irq ) + ); + + SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); +/* +** Enable the serial 1 port base address mapping +*/ + base_addr.as_uchar = 0; + base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3; + + SMC37c669_write_config( + SMC37c669_SERIAL0_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case SERIAL_1: + { + SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_SERIAL_IRQ_REGISTER irq; +/* +** Enable the serial 2 IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); + + irq.by_field.uart2_irq = + SMC37c669_RAW_DEVICE_IRQ( + SMC37c669_xlate_irq( local_config[ func ].irq ) + ); + + SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); +/* +** Enable the serial 2 port base address mapping +*/ + base_addr.as_uchar = 0; + base_addr.by_field.addr9_3 = local_config[ func ].port1 >> 3; + + SMC37c669_write_config( + SMC37c669_SERIAL1_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case PARALLEL_0: + { + SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; + SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; +/* +** Enable the parallel port DMA channel mapping +*/ + drq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); + + drq.by_field.ppt_drq = + SMC37c669_RAW_DEVICE_DRQ( + SMC37c669_xlate_drq( local_config[ func ].drq ) + ); + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_DRQ_INDEX, + drq.as_uchar + ); +/* +** Enable the parallel port IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); + + irq.by_field.ppt_irq = + SMC37c669_RAW_DEVICE_IRQ( + SMC37c669_xlate_irq( local_config[ func ].irq ) + ); + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_IRQ_INDEX, + irq.as_uchar + ); +/* +** Enable the parallel port base address mapping +*/ + base_addr.as_uchar = 0; + base_addr.by_field.addr9_2 = local_config[ func ].port1 >> 2; + + SMC37c669_write_config( + SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case FLOPPY_0: + { + SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; + SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; +/* +** Enable the floppy controller DMA channel mapping +*/ + drq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); + + drq.by_field.fdc_drq = + SMC37c669_RAW_DEVICE_DRQ( + SMC37c669_xlate_drq( local_config[ func ].drq ) + ); + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_DRQ_INDEX, + drq.as_uchar + ); +/* +** Enable the floppy controller IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); + + irq.by_field.fdc_irq = + SMC37c669_RAW_DEVICE_IRQ( + SMC37c669_xlate_irq( local_config[ func ].irq ) + ); + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_IRQ_INDEX, + irq.as_uchar + ); +/* +** Enable the floppy controller base address mapping +*/ + base_addr.as_uchar = 0; + base_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4; + + SMC37c669_write_config( + SMC37c669_FDC_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case IDE_0: + { + SMC37c669_IDE_ADDRESS_REGISTER ide_addr; +/* +** Enable the IDE alternate status base address mapping +*/ + ide_addr.as_uchar = 0; + ide_addr.by_field.addr9_4 = local_config[ func ].port2 >> 4; + + SMC37c669_write_config( + SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX, + ide_addr.as_uchar + ); +/* +** Enable the IDE controller base address mapping +*/ + ide_addr.as_uchar = 0; + ide_addr.by_field.addr9_4 = local_config[ func ].port1 >> 4; + + SMC37c669_write_config( + SMC37c669_IDE_BASE_ADDRESS_INDEX, + ide_addr.as_uchar + ); + ret_val = TRUE; + break; + } + } +/* +** Exit configuration mode and return +*/ + SMC37c669_config_mode( FALSE ); + + return ret_val; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function disables a device function within the +** SMC37c669 Super I/O controller. +** +** FORMAL PARAMETERS: +** +** func: +** Which function to disable +** +** RETURN VALUE: +** +** Return TRUE if the device function was disabled, otherwise, FALSE +** +** SIDE EFFECTS: +** +** {@description or none@} +** +** DESIGN: +** +** Disabling a function in the SMC37c669 device involves +** disabling all the function's mappings (port, irq, drq ...). +** A shadow copy of the device configuration is maintained +** in local storage so we won't worry aboving saving the +** current configuration information. +** +**-- +*/ +unsigned int SMC37c669_disable_device ( unsigned int func ) +{ + unsigned int ret_val = FALSE; + +/* +** Put the device into configuration mode +*/ + SMC37c669_config_mode( TRUE ); + switch ( func ) { + case SERIAL_0: + { + SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_SERIAL_IRQ_REGISTER irq; +/* +** Disable the serial 1 IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); + + irq.by_field.uart1_irq = 0; + + SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); +/* +** Disable the serial 1 port base address mapping +*/ + base_addr.as_uchar = 0; + SMC37c669_write_config( + SMC37c669_SERIAL0_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case SERIAL_1: + { + SMC37c669_SERIAL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_SERIAL_IRQ_REGISTER irq; +/* +** Disable the serial 2 IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); + + irq.by_field.uart2_irq = 0; + + SMC37c669_write_config( SMC37c669_SERIAL_IRQ_INDEX, irq.as_uchar ); +/* +** Disable the serial 2 port base address mapping +*/ + base_addr.as_uchar = 0; + + SMC37c669_write_config( + SMC37c669_SERIAL1_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case PARALLEL_0: + { + SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; + SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; +/* +** Disable the parallel port DMA channel mapping +*/ + drq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); + + drq.by_field.ppt_drq = 0; + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_DRQ_INDEX, + drq.as_uchar + ); +/* +** Disable the parallel port IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); + + irq.by_field.ppt_irq = 0; + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_IRQ_INDEX, + irq.as_uchar + ); +/* +** Disable the parallel port base address mapping +*/ + base_addr.as_uchar = 0; + + SMC37c669_write_config( + SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case FLOPPY_0: + { + SMC37c669_FDC_BASE_ADDRESS_REGISTER base_addr; + SMC37c669_PARALLEL_FDC_IRQ_REGISTER irq; + SMC37c669_PARALLEL_FDC_DRQ_REGISTER drq; +/* +** Disable the floppy controller DMA channel mapping +*/ + drq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); + + drq.by_field.fdc_drq = 0; + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_DRQ_INDEX, + drq.as_uchar + ); +/* +** Disable the floppy controller IRQ mapping +*/ + irq.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); + + irq.by_field.fdc_irq = 0; + + SMC37c669_write_config( + SMC37c669_PARALLEL_FDC_IRQ_INDEX, + irq.as_uchar + ); +/* +** Disable the floppy controller base address mapping +*/ + base_addr.as_uchar = 0; + + SMC37c669_write_config( + SMC37c669_FDC_BASE_ADDRESS_INDEX, + base_addr.as_uchar + ); + ret_val = TRUE; + break; + } + case IDE_0: + { + SMC37c669_IDE_ADDRESS_REGISTER ide_addr; +/* +** Disable the IDE alternate status base address mapping +*/ + ide_addr.as_uchar = 0; + + SMC37c669_write_config( + SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX, + ide_addr.as_uchar + ); +/* +** Disable the IDE controller base address mapping +*/ + ide_addr.as_uchar = 0; + + SMC37c669_write_config( + SMC37c669_IDE_BASE_ADDRESS_INDEX, + ide_addr.as_uchar + ); + ret_val = TRUE; + break; + } + } +/* +** Exit configuration mode and return +*/ + SMC37c669_config_mode( FALSE ); + + return ret_val; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function configures a device function within the +** SMC37c669 Super I/O controller. +** +** FORMAL PARAMETERS: +** +** func: +** Which device function +** +** port: +** I/O port for the function to use +** +** irq: +** IRQ for the device function to use +** +** drq: +** DMA channel for the device function to use +** +** RETURN VALUE: +** +** Returns TRUE if the device function was configured, +** otherwise, FALSE. +** +** SIDE EFFECTS: +** +** {@description or none@} +** +** DESIGN: +** +** If this function returns TRUE, the local shadow copy of +** the configuration is also updated. If the device function +** is currently disabled, only the local shadow copy is +** updated and the actual device function will be updated +** if/when is is enabled. +** +**-- +*/ +unsigned int SMC37c669_configure_device ( + unsigned int func, + int port, + int irq, + int drq ) +{ + struct DEVICE_CONFIG *cp; + +/* +** Check for a valid configuration +*/ + if ( ( cp = SMC37c669_get_config ( func ) ) != NULL ) { +/* +** Configuration is valid, update the local shadow copy +*/ + if ( ( drq & ~0xFF ) == 0 ) { + cp->drq = drq; + } + if ( ( irq & ~0xFF ) == 0 ) { + cp->irq = irq; + } + if ( ( port & ~0xFFFF ) == 0 ) { + cp->port1 = port; + } +/* +** If the device function is enabled, update the actual +** device configuration. +*/ + if ( SMC37c669_is_device_enabled( func ) ) { + SMC37c669_enable_device( func ); + } + return TRUE; + } + return FALSE; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function determines whether a device function +** within the SMC37c669 controller is enabled. +** +** FORMAL PARAMETERS: +** +** func: +** Which device function +** +** RETURN VALUE: +** +** Returns TRUE if the device function is enabled, otherwise, FALSE +** +** SIDE EFFECTS: +** +** {@description or none@} +** +** DESIGN: +** +** To check whether a device is enabled we will only look at +** the port base address mapping. According to the SMC37c669 +** specification, all of the port base address mappings are +** disabled if the addr<9:8> (bits <7:6> of the register) are +** zero. +** +**-- +*/ +static unsigned int SMC37c669_is_device_enabled ( unsigned int func ) +{ + unsigned char base_addr = 0; + unsigned int dev_ok = FALSE; + unsigned int ret_val = FALSE; +/* +** Enter configuration mode +*/ + SMC37c669_config_mode( TRUE ); + + switch ( func ) { + case SERIAL_0: + base_addr = + SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX ); + dev_ok = TRUE; + break; + case SERIAL_1: + base_addr = + SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX ); + dev_ok = TRUE; + break; + case PARALLEL_0: + base_addr = + SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX ); + dev_ok = TRUE; + break; + case FLOPPY_0: + base_addr = + SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX ); + dev_ok = TRUE; + break; + case IDE_0: + base_addr = + SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX ); + dev_ok = TRUE; + break; + } +/* +** If we have a valid device, check base_addr<7:6> to see if the +** device is enabled (mapped). +*/ + if ( ( dev_ok ) && ( ( base_addr & 0xC0 ) != 0 ) ) { +/* +** The mapping is not disabled, so assume that the function is +** enabled. +*/ + ret_val = TRUE; + } +/* +** Exit configuration mode +*/ + SMC37c669_config_mode( FALSE ); + + return ret_val; +} + + +#if 0 +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function retrieves the configuration information of a +** device function within the SMC37c699 Super I/O controller. +** +** FORMAL PARAMETERS: +** +** func: +** Which device function +** +** port: +** I/O port returned +** +** irq: +** IRQ returned +** +** drq: +** DMA channel returned +** +** RETURN VALUE: +** +** Returns TRUE if the device configuration was successfully +** retrieved, otherwise, FALSE. +** +** SIDE EFFECTS: +** +** The data pointed to by the port, irq, and drq parameters +** my be modified even if the configuration is not successfully +** retrieved. +** +** DESIGN: +** +** The device configuration is fetched from the local shadow +** copy. Any unused parameters will be set to -1. Any +** parameter which is not desired can specify the NULL +** pointer. +** +**-- +*/ +static unsigned int SMC37c669_get_device_config ( + unsigned int func, + int *port, + int *irq, + int *drq ) +{ + struct DEVICE_CONFIG *cp; + unsigned int ret_val = FALSE; +/* +** Check for a valid device configuration +*/ + if ( ( cp = SMC37c669_get_config( func ) ) != NULL ) { + if ( drq != NULL ) { + *drq = cp->drq; + ret_val = TRUE; + } + if ( irq != NULL ) { + *irq = cp->irq; + ret_val = TRUE; + } + if ( port != NULL ) { + *port = cp->port1; + ret_val = TRUE; + } + } + return ret_val; +} +#endif + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function displays the current state of the SMC37c699 +** Super I/O controller's device functions. +** +** FORMAL PARAMETERS: +** +** None +** +** RETURN VALUE: +** +** None +** +** SIDE EFFECTS: +** +** None +** +**-- +*/ +void SMC37c669_display_device_info ( void ) +{ + if ( SMC37c669_is_device_enabled( SERIAL_0 ) ) { + printk( " Serial 0: Enabled [ Port 0x%x, IRQ %d ]\n", + local_config[ SERIAL_0 ].port1, + local_config[ SERIAL_0 ].irq + ); + } + else { + printk( " Serial 0: Disabled\n" ); + } + + if ( SMC37c669_is_device_enabled( SERIAL_1 ) ) { + printk( " Serial 1: Enabled [ Port 0x%x, IRQ %d ]\n", + local_config[ SERIAL_1 ].port1, + local_config[ SERIAL_1 ].irq + ); + } + else { + printk( " Serial 1: Disabled\n" ); + } + + if ( SMC37c669_is_device_enabled( PARALLEL_0 ) ) { + printk( " Parallel: Enabled [ Port 0x%x, IRQ %d/%d ]\n", + local_config[ PARALLEL_0 ].port1, + local_config[ PARALLEL_0 ].irq, + local_config[ PARALLEL_0 ].drq + ); + } + else { + printk( " Parallel: Disabled\n" ); + } + + if ( SMC37c669_is_device_enabled( FLOPPY_0 ) ) { + printk( " Floppy Ctrl: Enabled [ Port 0x%x, IRQ %d/%d ]\n", + local_config[ FLOPPY_0 ].port1, + local_config[ FLOPPY_0 ].irq, + local_config[ FLOPPY_0 ].drq + ); + } + else { + printk( " Floppy Ctrl: Disabled\n" ); + } + + if ( SMC37c669_is_device_enabled( IDE_0 ) ) { + printk( " IDE 0: Enabled [ Port 0x%x, IRQ %d ]\n", + local_config[ IDE_0 ].port1, + local_config[ IDE_0 ].irq + ); + } + else { + printk( " IDE 0: Disabled\n" ); + } +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function puts the SMC37c669 Super I/O controller into, +** and takes it out of, configuration mode. +** +** FORMAL PARAMETERS: +** +** enable: +** TRUE to enter configuration mode, FALSE to exit. +** +** RETURN VALUE: +** +** None +** +** SIDE EFFECTS: +** +** The SMC37c669 controller may be left in configuration mode. +** +**-- +*/ +static void SMC37c669_config_mode( + unsigned int enable ) +{ + if ( enable ) { +/* +** To enter configuration mode, two writes in succession to the index +** port are required. If a write to another address or port occurs +** between these two writes, the chip does not enter configuration +** mode. Therefore, a spinlock is placed around the two writes to +** guarantee that they complete uninterrupted. +*/ + spinlock( &spl_atomic ); + wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); + wb( &SMC37c669->index_port, SMC37c669_CONFIG_ON_KEY ); + spinunlock( &spl_atomic ); + } + else { + wb( &SMC37c669->index_port, SMC37c669_CONFIG_OFF_KEY ); + } +} + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function reads an SMC37c669 Super I/O controller +** configuration register. This function assumes that the +** device is already in configuration mode. +** +** FORMAL PARAMETERS: +** +** index: +** Index value of configuration register to read +** +** RETURN VALUE: +** +** Data read from configuration register +** +** SIDE EFFECTS: +** +** None +** +**-- +*/ +static unsigned char SMC37c669_read_config( + unsigned char index ) +{ + unsigned char data; + + wb( &SMC37c669->index_port, index ); + data = rb( &SMC37c669->data_port ); + return data; +} + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function writes an SMC37c669 Super I/O controller +** configuration register. This function assumes that the +** device is already in configuration mode. +** +** FORMAL PARAMETERS: +** +** index: +** Index of configuration register to write +** +** data: +** Data to be written +** +** RETURN VALUE: +** +** None +** +** SIDE EFFECTS: +** +** None +** +**-- +*/ +static void SMC37c669_write_config( + unsigned char index, + unsigned char data ) +{ + wb( &SMC37c669->index_port, index ); + wb( &SMC37c669->data_port, data ); +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function initializes the local device +** configuration storage. This function assumes +** that the device is already in configuration +** mode. +** +** FORMAL PARAMETERS: +** +** None +** +** RETURN VALUE: +** +** None +** +** SIDE EFFECTS: +** +** Local storage for device configuration information +** is initialized. +** +**-- +*/ +static void SMC37c669_init_local_config ( void ) +{ + SMC37c669_SERIAL_BASE_ADDRESS_REGISTER uart_base; + SMC37c669_SERIAL_IRQ_REGISTER uart_irqs; + SMC37c669_PARALLEL_BASE_ADDRESS_REGISTER ppt_base; + SMC37c669_PARALLEL_FDC_IRQ_REGISTER ppt_fdc_irqs; + SMC37c669_PARALLEL_FDC_DRQ_REGISTER ppt_fdc_drqs; + SMC37c669_FDC_BASE_ADDRESS_REGISTER fdc_base; + SMC37c669_IDE_ADDRESS_REGISTER ide_base; + SMC37c669_IDE_ADDRESS_REGISTER ide_alt; + +/* +** Get serial port 1 base address +*/ + uart_base.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL0_BASE_ADDRESS_INDEX ); +/* +** Get IRQs for serial ports 1 & 2 +*/ + uart_irqs.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL_IRQ_INDEX ); +/* +** Store local configuration information for serial port 1 +*/ + local_config[SERIAL_0].port1 = uart_base.by_field.addr9_3 << 3; + local_config[SERIAL_0].irq = + SMC37c669_xlate_irq( + SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart1_irq ) + ); +/* +** Get serial port 2 base address +*/ + uart_base.as_uchar = + SMC37c669_read_config( SMC37c669_SERIAL1_BASE_ADDRESS_INDEX ); +/* +** Store local configuration information for serial port 2 +*/ + local_config[SERIAL_1].port1 = uart_base.by_field.addr9_3 << 3; + local_config[SERIAL_1].irq = + SMC37c669_xlate_irq( + SMC37c669_DEVICE_IRQ( uart_irqs.by_field.uart2_irq ) + ); +/* +** Get parallel port base address +*/ + ppt_base.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL0_BASE_ADDRESS_INDEX ); +/* +** Get IRQs for parallel port and floppy controller +*/ + ppt_fdc_irqs.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_IRQ_INDEX ); +/* +** Get DRQs for parallel port and floppy controller +*/ + ppt_fdc_drqs.as_uchar = + SMC37c669_read_config( SMC37c669_PARALLEL_FDC_DRQ_INDEX ); +/* +** Store local configuration information for parallel port +*/ + local_config[PARALLEL_0].port1 = ppt_base.by_field.addr9_2 << 2; + local_config[PARALLEL_0].irq = + SMC37c669_xlate_irq( + SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.ppt_irq ) + ); + local_config[PARALLEL_0].drq = + SMC37c669_xlate_drq( + SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.ppt_drq ) + ); +/* +** Get floppy controller base address +*/ + fdc_base.as_uchar = + SMC37c669_read_config( SMC37c669_FDC_BASE_ADDRESS_INDEX ); +/* +** Store local configuration information for floppy controller +*/ + local_config[FLOPPY_0].port1 = fdc_base.by_field.addr9_4 << 4; + local_config[FLOPPY_0].irq = + SMC37c669_xlate_irq( + SMC37c669_DEVICE_IRQ( ppt_fdc_irqs.by_field.fdc_irq ) + ); + local_config[FLOPPY_0].drq = + SMC37c669_xlate_drq( + SMC37c669_DEVICE_DRQ( ppt_fdc_drqs.by_field.fdc_drq ) + ); +/* +** Get IDE controller base address +*/ + ide_base.as_uchar = + SMC37c669_read_config( SMC37c669_IDE_BASE_ADDRESS_INDEX ); +/* +** Get IDE alternate status base address +*/ + ide_alt.as_uchar = + SMC37c669_read_config( SMC37c669_IDE_ALTERNATE_ADDRESS_INDEX ); +/* +** Store local configuration information for IDE controller +*/ + local_config[IDE_0].port1 = ide_base.by_field.addr9_4 << 4; + local_config[IDE_0].port2 = ide_alt.by_field.addr9_4 << 4; + local_config[IDE_0].irq = 14; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function returns a pointer to the local shadow +** configuration of the requested device function. +** +** FORMAL PARAMETERS: +** +** func: +** Which device function +** +** RETURN VALUE: +** +** Returns a pointer to the DEVICE_CONFIG structure for the +** requested function, otherwise, NULL. +** +** SIDE EFFECTS: +** +** {@description or none@} +** +**-- +*/ +static struct DEVICE_CONFIG *SMC37c669_get_config( unsigned int func ) +{ + struct DEVICE_CONFIG *cp = NULL; + + switch ( func ) { + case SERIAL_0: + cp = &local_config[ SERIAL_0 ]; + break; + case SERIAL_1: + cp = &local_config[ SERIAL_1 ]; + break; + case PARALLEL_0: + cp = &local_config[ PARALLEL_0 ]; + break; + case FLOPPY_0: + cp = &local_config[ FLOPPY_0 ]; + break; + case IDE_0: + cp = &local_config[ IDE_0 ]; + break; + } + return cp; +} + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function translates IRQs back and forth between ISA +** IRQs and SMC37c669 device IRQs. +** +** FORMAL PARAMETERS: +** +** irq: +** The IRQ to translate +** +** RETURN VALUE: +** +** Returns the translated IRQ, otherwise, returns -1. +** +** SIDE EFFECTS: +** +** {@description or none@} +** +**-- +*/ +static int SMC37c669_xlate_irq ( unsigned int irq ) +{ + int i, translated_irq = -1; + + if ( SMC37c669_IS_DEVICE_IRQ( irq ) ) { +/* +** We are translating a device IRQ to an ISA IRQ +*/ + for ( i = 0; ( SMC37c669_irq_table[i].device_irq != -1 ) || ( SMC37c669_irq_table[i].isa_irq != -1 ); i++ ) { + if ( irq == SMC37c669_irq_table[i].device_irq ) { + translated_irq = SMC37c669_irq_table[i].isa_irq; + break; + } + } + } + else { +/* +** We are translating an ISA IRQ to a device IRQ +*/ + for ( i = 0; ( SMC37c669_irq_table[i].isa_irq != -1 ) || ( SMC37c669_irq_table[i].device_irq != -1 ); i++ ) { + if ( irq == SMC37c669_irq_table[i].isa_irq ) { + translated_irq = SMC37c669_irq_table[i].device_irq; + break; + } + } + } + return translated_irq; +} + + +/* +**++ +** FUNCTIONAL DESCRIPTION: +** +** This function translates DMA channels back and forth between +** ISA DMA channels and SMC37c669 device DMA channels. +** +** FORMAL PARAMETERS: +** +** drq: +** The DMA channel to translate +** +** RETURN VALUE: +** +** Returns the translated DMA channel, otherwise, returns -1 +** +** SIDE EFFECTS: +** +** {@description or none@} +** +**-- +*/ +static int SMC37c669_xlate_drq ( unsigned int drq ) +{ + int i, translated_drq = -1; + + if ( SMC37c669_IS_DEVICE_DRQ( drq ) ) { +/* +** We are translating a device DMA channel to an ISA DMA channel +*/ + for ( i = 0; ( SMC37c669_drq_table[i].device_drq != -1 ) || ( SMC37c669_drq_table[i].isa_drq != -1 ); i++ ) { + if ( drq == SMC37c669_drq_table[i].device_drq ) { + translated_drq = SMC37c669_drq_table[i].isa_drq; + break; + } + } + } + else { +/* +** We are translating an ISA DMA channel to a device DMA channel +*/ + for ( i = 0; ( SMC37c669_drq_table[i].isa_drq != -1 ) || ( SMC37c669_drq_table[i].device_drq != -1 ); i++ ) { + if ( drq == SMC37c669_drq_table[i].isa_drq ) { + translated_drq = SMC37c669_drq_table[i].device_drq; + break; + } + } + } + return translated_drq; +} + +#if 0 +int smcc669_init ( void ) +{ + struct INODE *ip; + + allocinode( smc_ddb.name, 1, &ip ); + ip->dva = &smc_ddb; + ip->attr = ATTR$M_WRITE | ATTR$M_READ; + ip->len[0] = 0x30; + ip->misc = 0; + INODE_UNLOCK( ip ); + + return msg_success; +} + +int smcc669_open( struct FILE *fp, char *info, char *next, char *mode ) +{ + struct INODE *ip; +/* +** Allow multiple readers but only one writer. ip->misc keeps track +** of the number of writers +*/ + ip = fp->ip; + INODE_LOCK( ip ); + if ( fp->mode & ATTR$M_WRITE ) { + if ( ip->misc ) { + INODE_UNLOCK( ip ); + return msg_failure; /* too many writers */ + } + ip->misc++; + } +/* +** Treat the information field as a byte offset +*/ + *fp->offset = xtoi( info ); + INODE_UNLOCK( ip ); + + return msg_success; +} + +int smcc669_close( struct FILE *fp ) +{ + struct INODE *ip; + + ip = fp->ip; + if ( fp->mode & ATTR$M_WRITE ) { + INODE_LOCK( ip ); + ip->misc--; + INODE_UNLOCK( ip ); + } + return msg_success; +} + +int smcc669_read( struct FILE *fp, int size, int number, unsigned char *buf ) +{ + int i; + int length; + int nbytes; + struct INODE *ip; + +/* +** Always access a byte at a time +*/ + ip = fp->ip; + length = size * number; + nbytes = 0; + + SMC37c669_config_mode( TRUE ); + for ( i = 0; i < length; i++ ) { + if ( !inrange( *fp->offset, 0, ip->len[0] ) ) + break; + *buf++ = SMC37c669_read_config( *fp->offset ); + *fp->offset += 1; + nbytes++; + } + SMC37c669_config_mode( FALSE ); + return nbytes; +} + +int smcc669_write( struct FILE *fp, int size, int number, unsigned char *buf ) +{ + int i; + int length; + int nbytes; + struct INODE *ip; +/* +** Always access a byte at a time +*/ + ip = fp->ip; + length = size * number; + nbytes = 0; + + SMC37c669_config_mode( TRUE ); + for ( i = 0; i < length; i++ ) { + if ( !inrange( *fp->offset, 0, ip->len[0] ) ) + break; + SMC37c669_write_config( *fp->offset, *buf ); + *fp->offset += 1; + buf++; + nbytes++; + } + SMC37c669_config_mode( FALSE ); + return nbytes; +} +#endif + +void +SMC37c669_dump_registers(void) +{ + int i; + for (i = 0; i <= 0x29; i++) + printk("-- CR%02x : %02x\n", i, SMC37c669_read_config(i)); +} +/*+ + * ============================================================================ + * = SMC_init - SMC37c669 Super I/O controller initialization = + * ============================================================================ + * + * OVERVIEW: + * + * This routine configures and enables device functions on the + * SMC37c669 Super I/O controller. + * + * FORM OF CALL: + * + * SMC_init( ); + * + * RETURNS: + * + * Nothing + * + * ARGUMENTS: + * + * None + * + * SIDE EFFECTS: + * + * None + * +-*/ +void SMC669_Init ( void ) +{ + SMC37c669_CONFIG_REGS *SMC_base; + + if ( ( SMC_base = SMC37c669_detect( ) ) != NULL ) { + printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n", + (unsigned long) SMC_base ); +#if SMC_DEBUG + SMC37c669_config_mode( TRUE ); + SMC37c669_dump_registers( ); + SMC37c669_config_mode( FALSE ); + SMC37c669_display_device_info( ); +#endif + SMC37c669_disable_device( SERIAL_0 ); + SMC37c669_configure_device( + SERIAL_0, + COM1_BASE, + COM1_IRQ, + -1 + ); + SMC37c669_enable_device( SERIAL_0 ); + + SMC37c669_disable_device( SERIAL_1 ); + SMC37c669_configure_device( + SERIAL_1, + COM2_BASE, + COM2_IRQ, + -1 + ); + SMC37c669_enable_device( SERIAL_1 ); + + SMC37c669_disable_device( PARALLEL_0 ); + SMC37c669_configure_device( + PARALLEL_0, + PARP_BASE, + PARP_IRQ, + PARP_DRQ + ); + SMC37c669_enable_device( PARALLEL_0 ); + + SMC37c669_disable_device( FLOPPY_0 ); + SMC37c669_configure_device( + FLOPPY_0, + FDC_BASE, + FDC_IRQ, + FDC_DRQ + ); + SMC37c669_enable_device( FLOPPY_0 ); + + SMC37c669_disable_device( IDE_0 ); + +#if SMC_DEBUG + SMC37c669_config_mode( TRUE ); + SMC37c669_dump_registers( ); + SMC37c669_config_mode( FALSE ); + SMC37c669_display_device_info( ); +#endif + } + else { +#if SMC_DEBUG + printk( "No SMC37c669 Super I/O Controller found\n" ); +#endif + } +} + +#endif /* SX164 */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/t2.c linux/arch/alpha/kernel/t2.c --- v2.0.33/linux/arch/alpha/kernel/t2.c Wed Dec 31 16:00:00 1969 +++ linux/arch/alpha/kernel/t2.c Wed Jun 3 15:17:46 1998 @@ -0,0 +1,654 @@ +/* + * Code common to all T2 chips. + * + * Written by Jay A Estabrook (jestabro@amt.tay1.dec.com). + * December 1996. + * + * based on CIA code by David A Rusling (david.rusling@reo.mts.dec.com) + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct hwrpb_struct *hwrpb; +extern asmlinkage void wrmces(unsigned long mces); +extern asmlinkage unsigned long whami(void); + +#define CPUID whami() + +/* + * Machine check reasons. Defined according to PALcode sources + * (osf.h and platform.h). + */ +#define MCHK_K_TPERR 0x0080 +#define MCHK_K_TCPERR 0x0082 +#define MCHK_K_HERR 0x0084 +#define MCHK_K_ECC_C 0x0086 +#define MCHK_K_ECC_NC 0x0088 +#define MCHK_K_OS_BUGCHECK 0x008A +#define MCHK_K_PAL_BUGCHECK 0x0090 + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_ALPHA_T2 + +#ifdef DEBUG_CONF +# define DBG(args) printk args +#else +# define DBG(args) +#endif + +#ifdef DEBUG_MCHECK +# define DBGMC(args) printk args +#else +# define DBGMC(args) +#endif + +#define vulp volatile unsigned long * +#define vuip volatile unsigned int * + +static volatile unsigned int T2_mcheck_expected = 0; +static volatile unsigned int T2_mcheck_taken = 0; +static unsigned long T2_jd; + +#ifdef CONFIG_ALPHA_SRM_SETUP +unsigned int T2_DMA_WIN_BASE = T2_DMA_WIN_BASE_DEFAULT; +unsigned int T2_DMA_WIN_SIZE = T2_DMA_WIN_SIZE_DEFAULT; +unsigned long t2_sm_base; +#endif /* SRM_SETUP */ + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the T2_HAXR2 register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr, + unsigned char *type1) +{ + unsigned long addr; + + DBG(("mk_conf_addr(bus=%d,dfn=0x%x,where=0x%x,addr=0x%lx,type1=0x%x)\n", + bus, device_fn, where, pci_addr, type1)); + + if (bus == 0) { + int device = device_fn >> 3; + + /* type 0 configuration cycle: */ + + if (device > 8) { + DBG(("mk_conf_addr: device (%d)>20, returning -1\n", + device)); + return -1; + } + + *type1 = 0; +#if 0 + addr = (device_fn << 8) | (where); +#else + addr = (0x0800L << device) | ((device_fn & 7) << 8) | (where); +#endif + } else { + /* type 1 configuration cycle: */ + *type1 = 1; + addr = (bus << 16) | (device_fn << 8) | (where); + } + *pci_addr = addr; + DBG(("mk_conf_addr: returning pci_addr 0x%lx\n", addr)); + return 0; +} + + +static unsigned int conf_read(unsigned long addr, unsigned char type1) +{ + unsigned long flags; + unsigned int stat0, value; + unsigned int t2_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + + DBG(("conf_read(addr=0x%lx, type1=%d)\n", addr, type1)); + +#if 0 + /* reset status register to avoid losing errors: */ + stat0 = *((volatile unsigned int *)T2_IOCSR); + *((volatile unsigned int *)T2_IOCSR) = stat0; + mb(); + DBG(("conf_read: T2 IOCSR was 0x%x\n", stat0)); + /* if Type1 access, must set T2 CFG */ + if (type1) { + t2_cfg = *((unsigned int *)T2_IOC_CFG); + mb(); + *((unsigned int *)T2_IOC_CFG) = t2_cfg | 1; + DBG(("conf_read: TYPE1 access\n")); + } + mb(); + draina(); +#endif + T2_mcheck_expected = 1; + T2_mcheck_taken = 0; + mb(); + /* access configuration space: */ + value = *((volatile unsigned int *)addr); + mb(); + mb(); + if (T2_mcheck_taken) { + T2_mcheck_taken = 0; + value = 0xffffffffU; + mb(); + } + T2_mcheck_expected = 0; + mb(); + +#if 0 + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *((unsigned int *)T2_IOC_CFG) = t2_cfg & ~1; + mb(); + } +#endif + DBG(("conf_read(): finished\n")); + + restore_flags(flags); + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value, + unsigned char type1) +{ + unsigned long flags; + unsigned int stat0; + unsigned int t2_cfg = 0; /* to keep gcc quiet */ + + save_flags(flags); /* avoid getting hit by machine check */ + cli(); + +#if 0 + /* reset status register to avoid losing errors: */ + stat0 = *((volatile unsigned int *)T2_IOCSR); + *((volatile unsigned int *)T2_IOCSR) = stat0; + mb(); + DBG(("conf_write: T2 ERR was 0x%x\n", stat0)); + /* if Type1 access, must set T2 CFG */ + if (type1) { + t2_cfg = *((unsigned int *)T2_IOC_CFG); + mb(); + *((unsigned int *)T2_IOC_CFG) = t2_cfg | 1; + DBG(("conf_write: TYPE1 access\n")); + } + draina(); +#endif + T2_mcheck_expected = 1; + mb(); + /* access configuration space: */ + *((volatile unsigned int *)addr) = value; + mb(); + mb(); + T2_mcheck_expected = 0; + mb(); + +#if 0 + /* if Type1 access, must reset IOC CFG so normal IO space ops work */ + if (type1) { + *((unsigned int *)T2_IOC_CFG) = t2_cfg & ~1; + mb(); + } +#endif + DBG(("conf_write(): finished\n")); + restore_flags(flags); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr, type1) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + *value = 0xffffffff; + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1)) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + *value = conf_read(addr, type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x00; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x08; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = T2_CONF; + unsigned long pci_addr; + unsigned char type1; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr, &type1) < 0) { + return PCIBIOS_SUCCESSFUL; + } + addr |= (pci_addr << 5) + 0x18; + conf_write(addr, value << ((where & 3) * 8), type1); + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long t2_init(unsigned long mem_start, unsigned long mem_end) +{ + unsigned int t2_err; + struct percpu_struct *cpu; + int i; + +#if 0 + /* + * Set up error reporting. + */ + t2_err = *(vuip)T2_IOCSR ; + t2_err |= (0x1 << 7) ; /* master abort */ + *(vuip)T2_IOC_T2_ERR = t2_err ; + mb() ; +#endif + + printk("t2_init: HBASE was 0x%lx\n", *((unsigned long *)T2_HBASE)); +#if 0 + printk("t2_init: WBASE1=0x%lx WMASK1=0x%lx TBASE1=0x%lx\n", + *((vulp)T2_WBASE1), + *((vulp)T2_WMASK1), + *((vulp)T2_TBASE1)); + printk("t2_init: WBASE2=0x%lx WMASK2=0x%lx TBASE2=0x%lx\n", + *((vulp)T2_WBASE2), + *((vulp)T2_WMASK2), + *((vulp)T2_TBASE2)); +#endif + +#ifdef CONFIG_ALPHA_SRM_SETUP + /* check window 1 for enabled and mapped to 0 */ + if (((*(vulp)T2_WBASE1 & (3UL<<18)) == (2UL<<18)) && + (*(vuip)T2_TBASE1 == 0)) + { + T2_DMA_WIN_BASE = *(vulp)T2_WBASE1 & 0xfff00000UL; + T2_DMA_WIN_SIZE = *(vulp)T2_WMASK1 & 0xfff00000UL; + T2_DMA_WIN_SIZE += 0x00100000UL; +/* DISABLE window 2!! ?? */ +#if 1 + printk("t2_init: using Window 1 settings\n"); + printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)T2_WBASE1, + *(vulp)T2_WMASK1, + *(vulp)T2_TBASE1); +#endif + } + else /* check window 2 for enabled and mapped to 0 */ + if (((*(vulp)T2_WBASE2 & (3UL<<18)) == (2UL<<18)) && + (*(vuip)T2_TBASE2 == 0)) + { + T2_DMA_WIN_BASE = *(vulp)T2_WBASE2 & 0xfff00000UL; + T2_DMA_WIN_SIZE = *(vulp)T2_WMASK2 & 0xfff00000UL; + T2_DMA_WIN_SIZE += 0x00100000UL; +/* DISABLE window 1!! ?? */ +#if 1 + printk("t2_init: using Window 2 settings\n"); + printk("t2_init: BASE 0x%lx MASK 0x%lx TRANS 0x%lx\n", + *(vulp)T2_WBASE2, + *(vulp)T2_WMASK2, + *(vulp)T2_TBASE2); +#endif + } + else /* we must use our defaults... */ +#endif /* SRM_SETUP */ + { + /* + * Set up the PCI->physical memory translation windows. + * For now, window 2 is disabled. In the future, we may + * want to use it to do scatter/gather DMA. Window 1 + * goes at 1 GB and is 1 GB large. + */ + + /* WARNING!! must correspond to the DMA_WIN params!!! */ + *(vuip)T2_WBASE1 = 0x400807ffU; + *(vuip)T2_WMASK1 = 0x3ff00000U; + *(vuip)T2_TBASE1 = 0; + + *(vuip)T2_WBASE2 = 0x0; + *(vuip)T2_HBASE = 0x0; + } + + + /* + * check ASN in HWRPB for validity, report if bad + */ + if (hwrpb->max_asn != MAX_ASN) { + printk("T2_init: max ASN from HWRPB is bad (0x%lx)\n", + hwrpb->max_asn); + hwrpb->max_asn = MAX_ASN; + } + + /* + * Finally, clear the T2_HAE_3 register, which gets used + * for PCI Config Space accesses. That is the way + * we want to use it, and we do not want to depend on + * what ARC or SRM might have left behind... + */ + { + unsigned long t2_hae_1 = *((unsigned long *)T2_HAE_1); + unsigned long t2_hae_2 = *((unsigned long *)T2_HAE_2); + unsigned long t2_hae_3 = *((unsigned long *)T2_HAE_3); + unsigned long t2_hae_4 = *((unsigned long *)T2_HAE_4); +#if 1 + printk("T2_init: HAE1 was 0x%lx\n", t2_hae_1); + printk("T2_init: HAE2 was 0x%lx\n", t2_hae_2); + printk("T2_init: HAE3 was 0x%lx\n", t2_hae_3); + printk("T2_init: HAE4 was 0x%lx\n", t2_hae_4); +#endif +#ifdef CONFIG_ALPHA_SRM_SETUP + /* + sigh... For the SRM setup, unless we know apriori what the HAE + contents will be, we need to setup the arbitrary region bases + so we can test against the range of addresses and tailor the + region chosen for the SPARSE memory access. + + see include/asm-alpha/t2.h for the SPARSE mem read/write + */ + t2_sm_base = (t2_hae_1 << 27) & 0xf8000000UL; +#else /* SRM_SETUP */ + *((unsigned int *)T2_HAE_1) = 0; mb(); + *((unsigned int *)T2_HAE_2) = 0; mb(); + *((unsigned int *)T2_HAE_3) = 0; mb(); +#if 0 + *((unsigned int *)T2_HAE_4) = 0; mb(); +#endif +#endif /* SRM_SETUP */ + } + +#if 1 + if (hwrpb->nr_processors > 1) { + printk("T2_init: nr_processors 0x%lx\n", + hwrpb->nr_processors); + printk("T2_init: processor_size 0x%lx\n", + hwrpb->processor_size); + printk("T2_init: processor_offset 0x%lx\n", + hwrpb->processor_offset); + + cpu = (struct percpu_struct *) + ((char*)hwrpb + hwrpb->processor_offset); + + for (i = 0; i < hwrpb->nr_processors; i++ ) { + printk("T2_init: CPU 0x%x: flags 0x%lx type 0x%lx\n", + i, cpu->flags, cpu->type); + cpu = (struct percpu_struct *) + ((char *)cpu + hwrpb->processor_size); + } + } +#endif + + return mem_start; +} + +#define SIC_SEIC (1UL << 33) /* System Event Clear */ + +struct sable_cpu_csr *sable_cpu_regs[4] = { + (struct sable_cpu_csr *)CPU0_BASE, + (struct sable_cpu_csr *)CPU1_BASE, + (struct sable_cpu_csr *)CPU2_BASE, + (struct sable_cpu_csr *)CPU3_BASE, +}; +int t2_clear_errors(void) +{ + DBGMC(("???????? t2_clear_errors\n")); + + sable_cpu_regs[CPUID]->sic &= ~SIC_SEIC; + + /* + * clear cpu errors + */ + sable_cpu_regs[CPUID]->bcce |= sable_cpu_regs[CPUID]->bcce; + sable_cpu_regs[CPUID]->cbe |= sable_cpu_regs[CPUID]->cbe; + sable_cpu_regs[CPUID]->bcue |= sable_cpu_regs[CPUID]->bcue; + sable_cpu_regs[CPUID]->dter |= sable_cpu_regs[CPUID]->dter; + + *(unsigned long *)T2_CERR1 |= *(unsigned long *)T2_CERR1; + *(unsigned long *)T2_PERR1 |= *(unsigned long *)T2_PERR1; + + mb(); + mb(); + return 0; +} + +void t2_machine_check(unsigned long vector, unsigned long la_ptr, + struct pt_regs * regs) +{ + struct el_t2_logout_header *mchk_header; + struct el_t2_procdata_mcheck *mchk_procdata; + struct el_t2_sysdata_mcheck *mchk_sysdata; + unsigned long * ptr; + const char * reason; + char buf[128]; + long i; + + DBGMC(("t2_machine_check: vector=0x%lx la_ptr=0x%lx\n", + vector, la_ptr)); + + mchk_header = (struct el_t2_logout_header *)la_ptr; + + DBGMC(("t2_machine_check: susoffset=0x%lx procoffset=0x%lx\n", + mchk_header->elfl_sysoffset, mchk_header->elfl_procoffset)); + + mchk_sysdata = (struct el_t2_sysdata_mcheck *) + (la_ptr + mchk_header->elfl_sysoffset); + mchk_procdata = (struct el_t2_procdata_mcheck *) + (la_ptr + mchk_header->elfl_procoffset - sizeof(unsigned long)*32); + + DBGMC((" pc=0x%lx size=0x%x procoffset=0x%x sysoffset 0x%x\n", + regs->pc, mchk_header->elfl_size, mchk_header->elfl_procoffset, + mchk_header->elfl_sysoffset)); + DBGMC(("t2_machine_check: expected %d\n", T2_mcheck_expected)); +#ifdef DEBUG_DUMP + { + unsigned long *ptr; + int i; + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) { + printk(" +%lx %lx %lx\n", i*sizeof(long), + ptr[i], ptr[i+1]); + } + } +#endif /* DEBUG_DUMP */ + /* + * Check if machine check is due to a badaddr() and if so, + * ignore the machine check. + */ + mb(); + mb(); + if (T2_mcheck_expected/* && (mchk_sysdata->epic_dcsr && 0x0c00UL)*/) { + DBGMC(("T2 machine check expected\n")); + T2_mcheck_taken = 1; + t2_clear_errors(); + T2_mcheck_expected = 0; + mb(); + mb(); + wrmces(rdmces()|1);/* ??? */ + draina(); + return; + } + + switch ((unsigned int) mchk_header->elfl_error_type) { + case MCHK_K_TPERR: reason = "tag parity error"; break; + case MCHK_K_TCPERR: reason = "tag control parity error"; break; + case MCHK_K_HERR: reason = "generic hard error"; break; + case MCHK_K_ECC_C: reason = "correctable ECC error"; break; + case MCHK_K_ECC_NC: reason = "uncorrectable ECC error"; break; + case MCHK_K_OS_BUGCHECK: reason = "OS-specific PAL bugcheck"; break; + case MCHK_K_PAL_BUGCHECK: reason = "callsys in kernel mode"; break; + case 0x96: reason = "i-cache read retryable error"; break; + case 0x98: reason = "processor detected hard error"; break; + + /* system specific (these are for Alcor, at least): */ + case 0x203: reason = "system detected uncorrectable ECC error"; break; + case 0x205: reason = "parity error detected by T2"; break; + case 0x207: reason = "non-existent memory error"; break; + case 0x209: reason = "PCI SERR detected"; break; + case 0x20b: reason = "PCI data parity error detected"; break; + case 0x20d: reason = "PCI address parity error detected"; break; + case 0x20f: reason = "PCI master abort error"; break; + case 0x211: reason = "PCI target abort error"; break; + case 0x213: reason = "scatter/gather PTE invalid error"; break; + case 0x215: reason = "flash ROM write error"; break; + case 0x217: reason = "IOA timeout detected"; break; + case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break; + case 0x21b: reason = "EISA fail-safe timer timeout"; break; + case 0x21d: reason = "EISA bus time-out"; break; + case 0x21f: reason = "EISA software generated NMI"; break; + case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break; + default: + sprintf(buf, "reason for machine-check unknown (0x%x)", + (unsigned int) mchk_header->elfl_error_type); + reason = buf; + break; + } + wrmces(rdmces()|1); /* reset machine check pending flag */ + mb(); + + printk(KERN_CRIT " T2 machine check: %s%s\n", + reason, mchk_header->elfl_retry ? " (retryable)" : ""); + + /* dump the logout area to give all info: */ + + ptr = (unsigned long *)la_ptr; + for (i = 0; i < mchk_header->elfl_size / sizeof(long); i += 2) { + printk(KERN_CRIT " +%8lx %016lx %016lx\n", + i*sizeof(long), ptr[i], ptr[i+1]); + } +} + +#endif /* CONFIG_ALPHA_T2 */ diff -u --recursive --new-file v2.0.33/linux/arch/alpha/kernel/traps.c linux/arch/alpha/kernel/traps.c --- v2.0.33/linux/arch/alpha/kernel/traps.c Wed Aug 13 12:54:02 1997 +++ linux/arch/alpha/kernel/traps.c Wed Jun 3 15:17:46 1998 @@ -286,7 +286,7 @@ extern void alpha_write_fp_reg (unsigned long reg, unsigned long val); extern unsigned long alpha_read_fp_reg (unsigned long reg); - pc_addr = frame + 7 + 20 + 1; /* pc in PAL frame */ + pc_addr = frame + 7 + 20 + 3 /* em86 */ + 1; /* pc in PAL frame */ if (cnt >= 5 && jiffies - last_time > 5*HZ) { cnt = 0; @@ -336,7 +336,7 @@ case 16: case 17: case 18: /* a0-a2 in PAL frame */ - reg_addr += 7 + 20 + 3 + (reg - 16); + reg_addr += 7 + 20 + 3 /* em86 */ + 3 + (reg - 16); break; case 19: case 20: case 21: case 22: case 23: @@ -347,7 +347,7 @@ case 29: /* gp in PAL frame */ - reg_addr += 7 + 20 + 2; + reg_addr += 7 + 20 + 3 /* em86 */ + 2; break; case 30: diff -u --recursive --new-file v2.0.33/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c --- v2.0.33/linux/arch/alpha/math-emu/fp-emul.c Sun Aug 3 10:58:38 1997 +++ linux/arch/alpha/math-emu/fp-emul.c Wed Jun 3 15:17:46 1998 @@ -13,9 +13,9 @@ #define OPC_INTL 0x11 #define OPC_INTS 0x12 #define OPC_INTM 0x13 -#define OPC_FLTV 0x14 -#define OPC_FLTI 0x15 -#define OPC_FLTL 0x16 +#define OPC_FLTV 0x15 +#define OPC_FLTI 0x16 +#define OPC_FLTL 0x17 #define OPC_MISC 0x18 diff -u --recursive --new-file v2.0.33/linux/arch/alpha/math-emu/ieee-math.c linux/arch/alpha/math-emu/ieee-math.c --- v2.0.33/linux/arch/alpha/math-emu/ieee-math.c Thu Apr 11 23:49:30 1996 +++ linux/arch/alpha/math-emu/ieee-math.c Wed Jun 3 15:17:46 1998 @@ -137,9 +137,8 @@ static inline void mul64 (const unsigned long a, const unsigned long b, unsigned long c[2]) { - asm ("mulq %2,%3,%0\n\t" - "umulh %2,%3,%1" - : "r="(c[0]), "r="(c[1]) : "r"(a), "r"(b)); + c[0] = a * b; + asm ("umulh %1,%2,%0" : "=r"(c[1]) : "r"(a), "r"(b)); } @@ -276,7 +275,7 @@ { unsigned long res, sticky; - if (!a->f[0] && !a->f[1]) { + if (!a->e && !a->f[0] && !a->f[1]) { *b = (unsigned long) a->s << 63; /* return +/-0 */ return 0; } @@ -356,7 +355,7 @@ { unsigned long res, sticky; - if (!a->f[0] && !a->f[1]) { + if (!a->e && !a->f[0] && !a->f[1]) { *b = (unsigned long) a->s << 63; /* return +/-0 */ return 0; } @@ -384,7 +383,7 @@ a->e = -0x3ff; } } - if (a->e > 0x3ff) { + if (a->e >= 0x3ff) { res = FPCR_OVF | FPCR_INE; if (f & IEEE_TRAP_ENABLE_OVF) { a->e -= 0x600; /* scale down result by 2^alpha */ @@ -1143,12 +1142,9 @@ return 0; } op_c.s = op_a.s ^ op_b.s; - op_c.e = op_a.e + op_b.e; + op_c.e = op_a.e + op_b.e - 55; mul64(op_a.f[0], op_b.f[0], op_c.f); - normalize(&op_c); - op_c.e -= 55; /* drop the 55 original bits. */ - return round_s_ieee(f, &op_c, c); } @@ -1200,11 +1196,8 @@ return 0; } op_c.s = op_a.s ^ op_b.s; - op_c.e = op_a.e + op_b.e; + op_c.e = op_a.e + op_b.e - 55; mul64(op_a.f[0], op_b.f[0], op_c.f); - - normalize(&op_c); - op_c.e -= 55; /* drop the 55 original bits. */ return round_t_ieee(f, &op_c, c); } diff -u --recursive --new-file v2.0.33/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c --- v2.0.33/linux/arch/alpha/mm/init.c Tue Aug 20 23:18:07 1996 +++ linux/arch/alpha/mm/init.c Wed Jun 3 15:17:46 1998 @@ -26,6 +26,8 @@ extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); +struct thread_struct * original_pcb_ptr; + /* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a @@ -81,15 +83,19 @@ extern unsigned long free_area_init(unsigned long, unsigned long); -static void load_PCB(struct thread_struct * pcb) +static struct thread_struct * load_PCB(struct thread_struct * pcb) { + struct thread_struct *old_pcb; + __asm__ __volatile__( - "stq $30,0(%0)\n\t" - "bis %0,%0,$16\n\t" - "call_pal %1" - : /* no outputs */ + "stq $30,0(%1)\n\t" + "bis %1,%1,$16\n\t" + "call_pal %2\n\t" + "bis $0,$0,%0" + : "=r" (old_pcb) : "r" (pcb), "i" (PAL_swpctx) : "$0", "$1", "$16", "$22", "$23", "$24", "$25"); + return old_pcb; } /* @@ -107,10 +113,15 @@ start_mem = free_area_init(start_mem, end_mem); /* find free clusters, update mem_map[] accordingly */ - memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + memdesc = (struct memdesc_struct *) + (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); cluster = memdesc->cluster; for (i = memdesc->numclusters ; i > 0; i--, cluster++) { unsigned long pfn, nr; +#if 0 +printk("paging_init: cluster %d usage %ld start %ld size %ld\n", + i, cluster->usage, cluster->start_pfn, cluster->numpages); +#endif if (cluster->usage & 1) continue; pfn = cluster->start_pfn; @@ -134,7 +145,7 @@ init_task.tss.pal_flags = 1; /* set FEN, clear everything else */ init_task.tss.flags = 0; init_task.kernel_stack_page = INIT_STACK; - load_PCB(&init_task.tss); + original_pcb_ptr = load_PCB(&init_task.tss); flush_tlb_all(); return start_mem; diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.0.33/linux/arch/i386/kernel/entry.S Tue Sep 16 14:42:45 1997 +++ linux/arch/i386/kernel/entry.S Wed Jun 3 15:17:46 1998 @@ -182,10 +182,10 @@ cmpb SYMBOL_NAME(active_kernel_processor), %al; \ je 4f; \ 2: SMP_PROF_B \ - btl %al, SYMBOL_NAME(smp_invalidate_needed); \ + btl %eax, SYMBOL_NAME(smp_invalidate_needed); \ jnc 5f; \ lock; \ - btrl %al, SYMBOL_NAME(smp_invalidate_needed); \ + btrl %eax, SYMBOL_NAME(smp_invalidate_needed); \ jnc 5f; \ movl %cr3,%edx; \ movl %edx,%cr3; \ @@ -326,7 +326,7 @@ ALIGN 1: call SYMBOL_NAME(syscall_trace) movl ORIG_EAX(%esp),%eax - call SYMBOL_NAME(sys_call_table)(,%eax,4) + call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value #ifdef __SMP__ GET_PROCESSOR_OFFSET(%eax) diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- v2.0.33/linux/arch/i386/kernel/head.S Tue Mar 10 13:19:08 1998 +++ linux/arch/i386/kernel/head.S Wed Jun 3 15:17:46 1998 @@ -134,13 +134,20 @@ isnew: pushl %ecx # restore original EFLAGS popfl incl SYMBOL_NAME(have_cpuid) # we have CPUID + /* + * Technically we should use CPUID 0 to see if we have CPUID 1! + */ /* get processor type */ movl $1, %eax # Use the CPUID instruction to +#ifdef GAS_KNOWS_CPUID + cpuid # check the processor type +#else .byte 0x0f, 0xa2 # check the processor type +#endif movb %al, %cl # save reg for future use andb $0x0f,%ah # mask processor family movb %ah,SYMBOL_NAME(x86) - andb $0xf0, %eax # mask model + andb $0xf0, %al # mask model shrb $4, %al movb %al,SYMBOL_NAME(x86_model) andb $0x0f, %cl # mask mask revision @@ -148,7 +155,11 @@ movl %edx,SYMBOL_NAME(x86_capability) /* get vendor info */ xorl %eax, %eax # call CPUID with 0 -> return vendor ID +#ifdef GAS_KNOWS_CPUID + cpuid +#else .byte 0x0f, 0xa2 # CPUID +#endif movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars @@ -171,8 +182,8 @@ 2: movl %eax,%cr0 call check_x87 #ifdef __SMP__ - movb ready,%eax - orb %eax,%eax + movb ready,%al + orb %al,%al jz 3f movl $ SYMBOL_NAME(swapper_pg_dir), %eax movl %eax, %cr3 diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c --- v2.0.33/linux/arch/i386/kernel/ioport.c Wed Jan 11 11:10:53 1995 +++ linux/arch/i386/kernel/ioport.c Wed Jun 3 15:17:46 1998 @@ -54,7 +54,7 @@ return -EINVAL; if (from + num > IO_BITMAP_SIZE*32) return -EINVAL; - if (!suser()) + if (!suser() || securelevel > 0) return -EPERM; set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on); @@ -82,7 +82,7 @@ if (level > 3) return -EINVAL; - if (!suser()) + if (!suser() || securelevel > 0) return -EPERM; *(&eflags) = (eflags & 0xffffcfff) | (level << 12); return 0; diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v2.0.33/linux/arch/i386/kernel/process.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/kernel/process.c Wed Jun 3 15:17:46 1998 @@ -421,8 +421,9 @@ int i; if (current->ldt) { - free_page((unsigned long) current->ldt); + void * ldt = current->ldt; current->ldt = NULL; + vfree(ldt); for (i=1 ; itss)); if (p->ldt) - set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512); + set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, LDT_ENTRIES); else set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1); p->tss.bitmap = offsetof(struct thread_struct,io_bitmap); diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.0.33/linux/arch/i386/kernel/ptrace.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/kernel/ptrace.c Wed Jun 3 15:17:47 1998 @@ -2,6 +2,7 @@ /* By Ross Biro 1/23/92 */ /* edited by Linus Torvalds */ +#include /* CONFIG_MATH_EMULATION */ #include #include #include @@ -688,3 +689,9 @@ current->signal |= (1 << (current->exit_code - 1)); current->exit_code = 0; } + +void get_pt_regs_for_task(struct pt_regs *regs, struct task_struct *task) +{ + *regs = *(struct pt_regs *) (((unsigned char *) task->tss.esp0) - MAGICNUMBER); +} + diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c --- v2.0.33/linux/arch/i386/kernel/setup.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/kernel/setup.c Wed Jun 3 15:17:47 1998 @@ -47,6 +47,11 @@ char x86_vendor_id[13] = "unknown"; +unsigned char Cx86_step = 0; +static const char *Cx86_type[] = { + "unknown", "1.3", "1.4", "2.4", "2.5", "2.6", "2.7 or 3.7", "4.2" + }; + char ignore_irq13 = 0; /* set if exception 16 works */ char wp_works_ok = -1; /* set if paging hardware honours WP */ char hlt_works_ok = 1; /* set if the "hlt" instruction works */ @@ -226,6 +231,50 @@ return NULL; } +static const char * Cx86model(void) +{ + unsigned char nr6x86 = 0; + static const char *model[] = { + "unknown", "6x86", "6x86L", "6x86MX", "6x86MXi" + }; + switch (x86) { + case 5: + nr6x86 = ((x86_capability & (1 << 8)) ? 2 : 1); /* cx8 flag only on 6x86L */ + break; + case 6: + nr6x86 = 3; + break; + default: + nr6x86 = 0; + } + switch (x86_mask) { + case 0x03: + Cx86_step = 1; /* 6x86MX Rev 1.3 */ + break; + case 0x04: + Cx86_step = 2; /* 6x86MX Rev 1.4 */ + break; + case 0x14: + Cx86_step = 3; /* 6x86 Rev 2.4 */ + break; + case 0x15: + Cx86_step = 4; /* 6x86 Rev 2.5 */ + break; + case 0x16: + Cx86_step = 5; /* 6x86 Rev 2.6 */ + break; + case 0x17: + Cx86_step = 6; /* 6x86 Rev 2.7 or 3.7 */ + break; + case 0x22: + Cx86_step = 7; /* 6x86L Rev 4.2 */ + break; + default: + Cx86_step = 0; + } + return model[nr6x86]; +} + static const char * i686model(unsigned int nr) { static const char *model[] = { @@ -240,16 +289,20 @@ { const char *p = NULL; static char nbuf[12]; - switch (x86) { - case 4: - p = i486model(model); - break; - case 5: - p = i586model(model); - break; - case 6: - p = i686model(model); - break; + if (strncmp(x86_vendor_id, "Cyrix", 5) == 0) + p = Cx86model(); + else { + switch (x86) { + case 4: + p = i486model(model); + break; + case 5: + p = i586model(model); + break; + case 6: + p = i686model(model); + break; + } } if (p) return p; @@ -297,9 +350,16 @@ CD(x86_vendor_id)); if (CD(x86_mask)) - len += sprintf(buffer+len, - "stepping\t: %d\n", - CD(x86_mask)); + if (strncmp(x86_vendor_id, "Cyrix", 5) != 0) { + len += sprintf(buffer+len, + "stepping\t: %d\n", + CD(x86_mask)); + } + else { /* we have a Cyrix */ + len += sprintf(buffer+len, + "stepping\t: %s\n", + Cx86_type[Cx86_step]); + } else len += sprintf(buffer+len, "stepping\t: unknown\n"); diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v2.0.33/linux/arch/i386/kernel/smp.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/kernel/smp.c Wed Jun 3 15:17:47 1998 @@ -1025,7 +1025,7 @@ */ if(ct==1000) - printk("CPU #%d: previous IPI still not cleared after 10mS", smp_processor_id()); + printk("CPU #%d: previous IPI still not cleared after 10ms\n", smp_processor_id()); /* * Program the APIC to deliver the IPI diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c --- v2.0.33/linux/arch/i386/kernel/time.c Tue Dec 2 13:52:31 1997 +++ linux/arch/i386/kernel/time.c Wed Jun 3 15:17:47 1998 @@ -475,29 +475,30 @@ /* Don't use them if a suspend/resume could corrupt the timer value. This problem needs more debugging. */ - if (x86_capability & 16) { - do_gettimeoffset = do_fast_gettimeoffset; + if (x86_capability & 16) + if (strncmp(x86_vendor_id, "Cyrix", 5) != 0) { + do_gettimeoffset = do_fast_gettimeoffset; - if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) { - if( x86 == 5 ) { - if( x86_model == 0 ) { - /* turn on cycle counters during power down */ - __asm__ __volatile__ (" movl $0x83, %%ecx \n \ - .byte 0x0f,0x32 \n \ - orl $1,%%eax \n \ - .byte 0x0f,0x30 \n " - : : : "ax", "cx", "dx" ); - udelay(500); + if( strcmp( x86_vendor_id, "AuthenticAMD" ) == 0 ) { + if( x86 == 5 ) { + if( x86_model == 0 ) { + /* turn on cycle counters during power down */ + __asm__ __volatile__ (" movl $0x83, %%ecx \n \ + .byte 0x0f,0x32 \n \ + orl $1,%%eax \n \ + .byte 0x0f,0x30 \n " + : : : "ax", "cx", "dx" ); + udelay(500); + } } - } - } + } - /* read Pentium cycle counter */ - __asm__(".byte 0x0f,0x31" - :"=a" (init_timer_cc.low), - "=d" (init_timer_cc.high)); - irq0.handler = pentium_timer_interrupt; - } + /* read Pentium cycle counter */ + __asm__(".byte 0x0f,0x31" + :"=a" (init_timer_cc.low), + "=d" (init_timer_cc.high)); + irq0.handler = pentium_timer_interrupt; + } #endif setup_x86_irq(0, &irq0); } diff -u --recursive --new-file v2.0.33/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c --- v2.0.33/linux/arch/i386/kernel/vm86.c Tue Feb 25 12:22:17 1997 +++ linux/arch/i386/kernel/vm86.c Wed Jun 3 15:17:47 1998 @@ -104,7 +104,7 @@ -static do_vm86_irq_handling(int subfunction, int irqnumber); +static int do_vm86_irq_handling(int subfunction, int irqnumber); static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk); asmlinkage int sys_vm86old(struct vm86_struct * v86) @@ -611,7 +611,7 @@ int sig = irqnumber >> 8; int irq = irqnumber & 255; handle_irq_zombies(); - if (!suser()) return -EPERM; + if (!suser() || securelevel > 0) return -EPERM; if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM; if ( (irq<3) || (irq>15) ) return -EPERM; if (vm86_irqs[irq].tsk) return -EPERM; diff -u --recursive --new-file v2.0.33/linux/arch/m68k/kernel/head.S linux/arch/m68k/kernel/head.S --- v2.0.33/linux/arch/m68k/kernel/head.S Sat May 18 01:15:10 1996 +++ linux/arch/m68k/kernel/head.S Wed Jun 3 15:17:47 1998 @@ -65,6 +65,7 @@ * d4 - machine type */ +#include #include #include #include diff -u --recursive --new-file v2.0.33/linux/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c --- v2.0.33/linux/arch/ppc/kernel/setup.c Mon Jul 8 01:27:43 1996 +++ linux/arch/ppc/kernel/setup.c Wed Jun 3 15:17:47 1998 @@ -9,6 +9,7 @@ * bootup setup stuff.. */ +#include /* CONFIG_BLK_DEV_RAM */ #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.0.33/linux/drivers/block/floppy.c Tue Apr 8 08:47:45 1997 +++ linux/drivers/block/floppy.c Wed Jun 3 15:17:47 1998 @@ -118,7 +118,8 @@ static int FLOPPY_IRQ=6; static int FLOPPY_DMA=2; static int allowed_drive_mask = 0x33; - + +static int irqdma_allocated = 0; #include #include @@ -743,6 +744,9 @@ UDRS->select_date = jiffies; } } + + /* FIXME: we should be more graceful here */ + if (newdor & FLOPPY_MOTOR_MASK) floppy_grab_irq_and_dma(); if (olddor & FLOPPY_MOTOR_MASK) @@ -802,10 +806,11 @@ unsigned long flags; if (!usage_count){ - printk("trying to lock fdc while usage count=0\n"); + printk(KERN_ERR "trying to lock fdc while usage count=0\n"); return -1; } - floppy_grab_irq_and_dma(); + if(floppy_grab_irq_and_dma()==-1) + return -EBUSY; INT_OFF; while (fdc_busy && NO_SIGNAL) interruptible_sleep_on(&fdc_wait); @@ -4084,6 +4089,7 @@ fd_outb(FDCS->dor, FD_DOR); fdc = 0; fd_enable_irq(); + irqdma_allocated=1; return 0; } @@ -4102,10 +4108,14 @@ return; } INT_ON; - fd_disable_dma(); - fd_free_dma(); - fd_disable_irq(); - fd_free_irq(); + if(irqdma_allocated) + { + fd_disable_dma(); + fd_free_dma(); + fd_disable_irq(); + fd_free_irq(); + irqdma_allocated=0; + } set_dor(0, ~0, 8); #if N_FDC > 1 @@ -4248,10 +4258,12 @@ void floppy_eject(void) { int dummy; - floppy_grab_irq_and_dma(); - lock_fdc(MAXTIMEOUT,0); - dummy=fd_eject(0); - process_fd_request(); - floppy_release_irq_and_dma(); + if(floppy_grab_irq_and_dma()==0) + { + lock_fdc(MAXTIMEOUT,0); + dummy=fd_eject(0); + process_fd_request(); + floppy_release_irq_and_dma(); + } } #endif diff -u --recursive --new-file v2.0.33/linux/drivers/block/genhd.c linux/drivers/block/genhd.c --- v2.0.33/linux/drivers/block/genhd.c Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/genhd.c Wed Jun 3 15:17:47 1998 @@ -102,6 +102,7 @@ static inline int is_extended_partition(struct partition *p) { return (SYS_IND(p) == DOS_EXTENDED_PARTITION || + SYS_IND(p) == WIN98_EXTENDED_PARTITION || SYS_IND(p) == LINUX_EXTENDED_PARTITION); } diff -u --recursive --new-file v2.0.33/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c --- v2.0.33/linux/drivers/block/ide-cd.c Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/ide-cd.c Wed Jun 3 15:17:47 1998 @@ -1980,7 +1980,7 @@ pc.c[7] = ((nblocks>>8) & 0xff); pc.c[6] = ((nblocks>>16) & 0xff); if (format <= 1) - pc.c[9] = 0xf0; + pc.c[9] = 0xf8; else pc.c[9] = 0x10; @@ -2409,7 +2409,7 @@ kfree (buf); return stat; } - + case CDROMREADRAW: case CDROMREADMODE1: case CDROMREADMODE2: { struct cdrom_msf msf; @@ -2420,10 +2420,13 @@ if (cmd == CDROMREADMODE1) { blocksize = CD_FRAMESIZE; format = 2; - } else { - blocksize = CD_FRAMESIZE_RAW0; - format = 3; - } + } else if (cmd == CDROMREADMODE2) { + blocksize = CD_FRAMESIZE_RAW0; + format = 3; + } else { + blocksize = CD_FRAMESIZE_RAW; + format = 0; + } stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize); if (stat) return stat; @@ -2434,16 +2437,16 @@ msf.cdmsf_sec0, msf.cdmsf_frame0); - /* Make sure the TOC is up to date. */ - stat = cdrom_read_toc (drive, NULL); + /* DON'T make sure the TOC is up to date. */ + /* stat = cdrom_read_toc (drive, NULL); if (stat) return stat; toc = drive->cdrom_info.toc; if (lba < 0 || lba >= toc->capacity) - return -EINVAL; + return -EINVAL; */ - buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL); + buf = (char *) kmalloc (CD_FRAMESIZE_RAW, GFP_KERNEL); if (buf == NULL) return -ENOMEM; diff -u --recursive --new-file v2.0.33/linux/drivers/block/ide.c linux/drivers/block/ide.c --- v2.0.33/linux/drivers/block/ide.c Mon Aug 4 11:45:55 1997 +++ linux/drivers/block/ide.c Wed Jun 3 15:17:47 1998 @@ -272,6 +272,7 @@ * acknowledge media change on removable drives * add work-around for BMI drives * remove "LBA" from boot messages + * Version 5.53.1 add UDMA "CRC retry" support * * Some additional driver compile-time options are in ide.h * @@ -602,6 +603,11 @@ unsigned long chs_sects = id->cyls * id->heads * id->sectors; unsigned long _10_percent = chs_sects / 10; + /* very large drives (8GB+) may lie about the number of cylinders */ + if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) { + id->cyls = lba_sects / (16 * 63); /* correct cyls */ + return 1; /* lba_capacity is our only option */ + } /* perform a rough sanity check on lba_sects: within 10% is "okay" */ if ((lba_sects - chs_sects) < _10_percent) return 1; /* lba_capacity is good */ @@ -636,6 +642,7 @@ /* Determine capacity, and use LBA if the drive properly supports it */ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) { if (id->lba_capacity >= capacity) { + drive->cyl = id->lba_capacity / (drive->head * drive->sect); capacity = id->lba_capacity; drive->select.b.lba = 1; } @@ -1043,12 +1050,15 @@ } else { if (drive->media == ide_disk && (stat & ERR_STAT)) { /* err has different meaning on cdrom and tape */ - if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return; /* some newer drives don't support WIN_SPECIFY */ + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) + ; /* UDMA crc error -- just retry the operation */ + else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ rq->errors = ERROR_MAX; else if (err & TRK0_ERR) /* help it find track zero */ rq->errors |= ERROR_RECAL; - else if (err & MC_ERR) - drive->special.b.mc = 1; } if ((stat & DRQ_STAT) && rq->cmd != WRITE) try_to_flush_leftover_data(drive); @@ -2270,7 +2280,7 @@ { byte args[4], *argbuf = args; int argsize = 4; - if (!suser()) return -EACCES; + if (!suser() || securelevel > 0) return -EACCES; if (NULL == (void *) arg) { err = ide_do_drive_cmd(drive, &rq, ide_wait); } else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) { @@ -2561,14 +2571,16 @@ drive->head = id->heads; drive->sect = id->sectors; } + + /* calculate drive capacity, and select LBA if possible */ + (void) current_capacity (drive); + /* Correct the number of cyls if the bios value is too small */ if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) { if (drive->cyl > drive->bios_cyl) drive->bios_cyl = drive->cyl; } - (void) current_capacity (drive); /* initialize LBA selection */ - if (!strncmp(id->model, "BMI ", 4) && strstr(id->model, " ENHANCED IDE ") && drive->select.b.lba) @@ -2587,8 +2599,12 @@ drive->special.b.set_multmode = 1; } if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) { - if (!(HWIF(drive)->dmaproc(ide_dma_check, drive))) - printk(", DMA"); + if (!(HWIF(drive)->dmaproc(ide_dma_check, drive))) { + if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7)) + printk(", UDMA"); + else + printk(", DMA"); + } } printk("\n"); } @@ -2618,11 +2634,12 @@ { int hd_status, rc; unsigned long timeout; - int irqs = 0; + unsigned long irqs_on = 0; + int irq_off; if (!HWIF(drive)->irq) { /* already got an IRQ? */ probe_irq_off(probe_irq_on()); /* clear dangling irqs */ - irqs = probe_irq_on(); /* start monitoring irqs */ + irqs_on = probe_irq_on(); /* start monitoring irqs */ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */ } @@ -2636,8 +2653,8 @@ #if CONFIG_BLK_DEV_PROMISE if (IS_PROMISE_DRIVE) { if (promise_cmd(drive,PROMISE_IDENTIFY)) { - if (irqs) - (void) probe_irq_off(irqs); + if (irqs_on) + (void) probe_irq_off(irqs_on); return 1; } } else @@ -2647,8 +2664,8 @@ timeout += jiffies; do { if (jiffies > timeout) { - if (irqs) - (void) probe_irq_off(irqs); + if (irqs_on) + (void) probe_irq_off(irqs_on); return 1; /* drive timed-out */ } delay_50ms(); /* give drive a breather */ @@ -2666,18 +2683,18 @@ } else rc = 2; /* drive refused ID */ if (!HWIF(drive)->irq) { - irqs = probe_irq_off(irqs); /* get our irq number */ - if (irqs > 0) { - HWIF(drive)->irq = irqs; /* save it for later */ - irqs = probe_irq_on(); + irq_off = probe_irq_off(irqs_on); /* get our irq number */ + if (irq_off > 0) { + HWIF(drive)->irq = irq_off; /* save it for later */ + irqs_on = probe_irq_on(); OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */ udelay(5); - (void) probe_irq_off(irqs); + (void) probe_irq_off(irqs_on); (void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */ (void) GET_STAT(); /* clear drive IRQ */ } else { /* Mmmm.. multiple IRQs.. don't know which was ours */ - printk("%s: IRQ probe failed (%d)\n", drive->name, irqs); + printk("%s: IRQ probe failed (%d)\n", drive->name, irq_off); #ifdef CONFIG_BLK_DEV_CMD640 #ifdef CMD640_DUMP_REGS if (HWIF(drive)->chipset == ide_cmd640) { diff -u --recursive --new-file v2.0.33/linux/drivers/block/ide.h linux/drivers/block/ide.h --- v2.0.33/linux/drivers/block/ide.h Wed Oct 15 15:22:14 1997 +++ linux/drivers/block/ide.h Wed Jun 3 15:17:47 1998 @@ -65,6 +65,10 @@ * "No user-serviceable parts" beyond this point :) *****************************************************************************/ +#if defined(CONFIG_BLK_DEV_IDESCSI) && !defined(CONFIG_SCSI) +#error "SCSI must also be selected" +#endif + typedef unsigned char byte; /* used everywhere */ /* diff -u --recursive --new-file v2.0.33/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.0.33/linux/drivers/block/md.c Sat Jun 29 14:04:00 1996 +++ linux/drivers/block/md.c Wed Jun 3 15:17:47 1998 @@ -202,7 +202,7 @@ if (inode->i_count>1 || md_dev[minor].busy>1) /* ioctl : one open channel */ { - printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, md_dev[minor].busy); + printk ("STOP_MD md%x failed : i_count=%ld, busy=%d\n", minor, inode->i_count, md_dev[minor].busy); return -EBUSY; } diff -u --recursive --new-file v2.0.33/linux/drivers/block/raid0.c linux/drivers/block/raid0.c --- v2.0.33/linux/drivers/block/raid0.c Sun May 19 21:50:46 1996 +++ linux/drivers/block/raid0.c Wed Jun 3 15:17:47 1998 @@ -26,7 +26,7 @@ #define MD_DRIVER #define MD_PERSONALITY -static void create_strip_zones (int minor, struct md_dev *mddev) +static int create_strip_zones (int minor, struct md_dev *mddev) { int i, j, c=0; int current_offset=0; @@ -50,8 +50,8 @@ c=0; } - data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones, - GFP_KERNEL); + if ((data->strip_zone=vmalloc(sizeof(struct strip_zone)*data->nr_strip_zones)) == NULL) + return 1; data->smallest=NULL; @@ -81,6 +81,7 @@ data->strip_zone[i-1].size) : 0; current_offset=smallest_by_zone->size; } + return 0; } static int raid0_run (int minor, struct md_dev *mddev) @@ -90,16 +91,18 @@ MOD_INC_USE_COUNT; - mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL); + if ((mddev->private=vmalloc (sizeof (struct raid0_data))) == NULL) return 1; data=(struct raid0_data *) mddev->private; - create_strip_zones (minor, mddev); + if (create_strip_zones (minor, mddev)) return 1; nb_zone=data->nr_zones= md_size[minor]/data->smallest->size + (md_size[minor]%data->smallest->size ? 1 : 0); - - data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL); + + printk ("raid0 : Allocating %d bytes for hash.\n",sizeof(struct raid0_hash)*nb_zone); + if ((data->hash_table=vmalloc (sizeof (struct raid0_hash)*nb_zone)) == NULL) + return 1; size=data->strip_zone[cur].size; @@ -142,9 +145,9 @@ { struct raid0_data *data=(struct raid0_data *) mddev->private; - kfree (data->hash_table); - kfree (data->strip_zone); - kfree (data); + vfree (data->hash_table); + vfree (data->strip_zone); + vfree (data); MOD_DEC_USE_COUNT; return 0; diff -u --recursive --new-file v2.0.33/linux/drivers/block/rd.c linux/drivers/block/rd.c --- v2.0.33/linux/drivers/block/rd.c Tue Jul 2 09:08:41 1996 +++ linux/drivers/block/rd.c Wed Jun 3 15:17:47 1998 @@ -422,7 +422,7 @@ /* * This routine loads in the ramdisk image. */ -static void rd_load_image(kdev_t device,int offset) +static void rd_load_image(kdev_t device,int offset, int unit) { struct inode inode, out_inode; struct file infile, outfile; @@ -433,7 +433,7 @@ unsigned short rotate = 0; char rotator[4] = { '|' , '/' , '-' , '\\' }; - ram_device = MKDEV(MAJOR_NR, 0); + ram_device = MKDEV(MAJOR_NR, unit); memset(&infile, 0, sizeof(infile)); memset(&inode, 0, sizeof(inode)); @@ -500,7 +500,7 @@ successful_load: invalidate_buffers(device); - ROOT_DEV = MKDEV(MAJOR_NR,0); + ROOT_DEV = MKDEV(MAJOR_NR,unit); done: if (infile.f_op->release) @@ -509,12 +509,21 @@ } -void rd_load() +static void rd_load_disk(int n) { +#ifdef CONFIG_BLK_DEV_INITRD + extern kdev_t real_root_dev; +#endif + if (rd_doload == 0) return; - if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return; + if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR +#ifdef CONFIG_BLK_DEV_INITRD + && MAJOR(real_root_dev) != FLOPPY_MAJOR +#endif + ) + return; if (rd_prompt) { #ifdef CONFIG_BLK_DEV_FD @@ -525,15 +534,24 @@ wait_for_keypress(); } - rd_load_image(ROOT_DEV,rd_image_start); + rd_load_image(ROOT_DEV,rd_image_start,n); + +} +void rd_load(void) +{ + rd_load_disk(0); } +void rd_load_secondary(void) +{ + rd_load_disk(1); +} #ifdef CONFIG_BLK_DEV_INITRD void initrd_load(void) { - rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),0); + rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),0,0); } #endif @@ -658,6 +676,13 @@ crd_load(struct file * fp, struct file *outfp) { int result; + + insize = 0; /* valid bytes in inbuf */ + inptr = 0; /* index of next byte to be processed in inbuf */ + outcnt = 0; /* bytes in output buffer */ + exit_code = 0; + bytes_out = 0; + crc = 0xFFFFFFFF; crd_infp = fp; crd_outfp = outfp; diff -u --recursive --new-file v2.0.33/linux/drivers/block/triton.c linux/drivers/block/triton.c --- v2.0.33/linux/drivers/block/triton.c Sat Sep 21 23:54:08 1996 +++ linux/drivers/block/triton.c Wed Jun 3 15:17:47 1998 @@ -247,14 +247,21 @@ static int config_drive_for_dma (ide_drive_t *drive) { const char **list; - struct hd_driveid *id = drive->id; + if (id && (id->capability & 1)) { - /* Enable DMA on any drive that supports mword2 DMA */ - if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) { - drive->using_dma = 1; - return 0; /* DMA enabled */ - } + /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */ + if (id->field_valid & 4) /* UltraDMA */ + if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) { + drive->using_dma = 1; + return 0; /* dma enabled */ + } + /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) { + drive->using_dma = 1; + return 0; /* dma enabled */ + } /* Consult the list of known "good" drives */ list = good_dma_drives; while (*list) { diff -u --recursive --new-file v2.0.33/linux/drivers/block/xd.c linux/drivers/block/xd.c --- v2.0.33/linux/drivers/block/xd.c Sun Sep 8 09:50:20 1996 +++ linux/drivers/block/xd.c Wed Jun 3 15:17:47 1998 @@ -20,6 +20,14 @@ * * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu * + * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl + * Fixed some problems with disk initialization and module initiation. + * Recovered DMA access. Abridged messages. Added support for DTC5051CX, + * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings. + * Added support for manual geometry setting (except Seagate controllers) + * in form: + * xd_geo=,,[,,,] + * Extended ioctl() support. */ #include @@ -28,7 +36,9 @@ #include #include #include +#include #include +#include #include #include @@ -39,6 +49,13 @@ #define MAJOR_NR XT_DISK_MAJOR #include +#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using + "nodma" module option */ +#define XD_INIT_DISK_DELAY 3 /* 30 ms delay during disk initialization */ + +/* Above may need to be increased if a problem with the 2nd drive detection + (ST11M controller) or resetting a controler (WD) appears */ + XD_INFO xd_info[XD_MAXDRIVES]; /* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS @@ -65,26 +82,49 @@ NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */ +#include +/* coppied from floppy.c */ +static inline int __get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} +#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size)) +#define xd_dma_mem_free(addr, size) free_pages(addr, __get_order(size)) +static char *xd_dma_buffer = 0; + static XD_SIGNATURE xd_sigs[] = { { 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */ + { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */ { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */ - { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */ - { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ + { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," WD 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */ + { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," WD 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ + { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," WD 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */ + { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */ { 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */ { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */ { 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */ + { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ }; static u_char *xd_bases[] = { (u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0xCC000, - (u_char *) 0xCE000,(u_char *) 0xD0000,(u_char *) 0xD8000, + (u_char *) 0xCE000,(u_char *) 0xD0000,(u_char *) 0xD2000, + (u_char *) 0xD4000,(u_char *) 0xD6000,(u_char *) 0xD8000, + (u_char *) 0xDA000,(u_char *) 0xDC000,(u_char *) 0xDE000, (u_char *) 0xE0000 }; -static struct hd_struct xd[XD_MAXDRIVES << 6]; +static struct hd_struct xd_struct[XD_MAXDRIVES << 6]; static int xd_sizes[XD_MAXDRIVES << 6], xd_access[XD_MAXDRIVES] = { 0, 0 }; static int xd_blocksizes[XD_MAXDRIVES << 6]; static struct gendisk xd_gendisk = { @@ -98,7 +138,7 @@ #else xd_geninit, /* init function */ #endif - xd, /* hd struct */ + xd_struct, /* hd struct */ xd_sizes, /* block sizes */ 0, /* number */ (void *) xd_info, /* internal */ @@ -118,9 +158,22 @@ }; static struct wait_queue *xd_wait_int = NULL, *xd_wait_open = NULL; static u_char xd_valid[XD_MAXDRIVES] = { 0,0 }; -static u_char xd_drives = 0, xd_irq = 0, xd_dma = 0, xd_maxsectors; +static u_char xd_drives = 0, xd_irq = 5, xd_dma = 3, xd_maxsectors; static u_char xd_override = 0, xd_type = 0; -static u_short xd_iobase = 0; +static u_short xd_iobase = 0x320; +static int xd_geo[XD_MAXDRIVES*3] = { 0,0,0,0,0,0 }; + +static int xd[5]; + +static volatile int xdc_busy = 0; +static struct wait_queue *xdc_wait = NULL; + +typedef void (*timeout_fn)(unsigned long); +static struct timer_list xd_timer = { NULL, NULL, 0, 0, (timeout_fn) xd_wakeup }, + xd_watchdog_int = { NULL, NULL, 0, 0, (timeout_fn) xd_watchdog }; + +static volatile u_char xd_error; +static int nodma = XD_DONT_USE_DMA; /* xd_init: register the block device number and set up pointer tables */ int xd_init (void) @@ -153,6 +206,7 @@ for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++) if (!memcmp(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) { *controller = j; + xd_type = j; *address = xd_bases[i]; found++; } @@ -168,6 +222,11 @@ if (xd_detect(&controller,&address)) { printk("xd_geninit: detected a%s controller (type %d) at address %p\n",xd_sigs[controller].name,controller,address); + if (check_region(xd_iobase,4)) { + printk("xd: Ports at 0x%x are not available\n",xd_iobase); + return; + } + request_region(xd_iobase,4,"xd"); if (controller) xd_sigs[controller].init_controller(address); xd_drives = xd_initdrives(xd_sigs[controller].init_drive); @@ -176,6 +235,8 @@ for (i = 0; i < xd_drives; i++) printk("xd_geninit: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors); + } + if (xd_drives) { if (!request_irq(xd_irq,xd_interrupt_handler, 0, "XT harddisk", NULL)) { if (request_dma(xd_dma,"xd")) { printk("xd_geninit: unable to get DMA%d\n",xd_dma); @@ -187,7 +248,7 @@ } for (i = 0; i < xd_drives; i++) { - xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors; + xd_struct[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors; xd_valid[i] = 1; } @@ -206,6 +267,10 @@ while (!xd_valid[dev]) sleep_on(&xd_wait_open); +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif /* MODULE */ + xd_access[dev]++; return (0); @@ -221,13 +286,15 @@ int code; sti(); + if (xdc_busy) + return; while (code = 0, CURRENT) { INIT_REQUEST; /* do some checking on the request structure */ if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors - <= xd[MINOR(CURRENT->rq_dev)].nr_sects) { - block = CURRENT->sector + xd[MINOR(CURRENT->rq_dev)].start_sect; + <= xd_struct[MINOR(CURRENT->rq_dev)].nr_sects) { + block = CURRENT->sector + xd_struct[MINOR(CURRENT->rq_dev)].start_sect; count = CURRENT->nr_sectors; switch (CURRENT->cmd) { @@ -244,6 +311,18 @@ } } +static int write_fs_long (unsigned long useraddr, long value) +{ + int err; + + if (NULL == (long *)useraddr) + return -EINVAL; + if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long)))) + return err; + put_user((unsigned)value, (long *) useraddr); + return 0; +} + /* xd_ioctl: handle device ioctl's */ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) { @@ -259,7 +338,7 @@ put_user(xd_info[dev].heads, &geometry->heads); put_user(xd_info[dev].sectors, &geometry->sectors); put_user(xd_info[dev].cylinders, &geometry->cylinders); - put_user(xd[MINOR(inode->i_rdev)].start_sect,&geometry->start); + put_user(xd_struct[MINOR(inode->i_rdev)].start_sect,&geometry->start); return (0); } @@ -273,11 +352,13 @@ return -EINVAL; read_ahead[MAJOR(inode->i_rdev)] = arg; return 0; + case BLKRAGET: + return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]); case BLKGETSIZE: if (arg) { if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)))) return (err); - put_user(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg); + put_user(xd_struct[MINOR(inode->i_rdev)].nr_sects,(long *) arg); return (0); } @@ -289,7 +370,21 @@ fsync_dev(inode->i_rdev); invalidate_buffers(inode->i_rdev); return 0; - + case HDIO_SET_DMA: + if (!suser()) + return -EACCES; + if (xdc_busy) + return -EBUSY; + nodma = !arg; + if (nodma && xd_dma_buffer) { + xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); + xd_dma_buffer = 0; + } + return 0; + case HDIO_GET_DMA: + return write_fs_long(arg, !nodma); + case HDIO_GET_MULTCOUNT: + return write_fs_long(arg, xd_maxsectors); case BLKRRPART: return (xd_reread_partitions(inode->i_rdev)); RO_IOCTLS(inode->i_rdev,arg); @@ -305,6 +400,11 @@ if (dev < xd_drives) { sync_dev(inode->i_rdev); xd_access[dev]--; + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + } } @@ -343,13 +443,17 @@ { u_char cmdblk[6],sense[4]; u_short track,cylinder; - u_char head,sector,control,mode,temp; + u_char head,sector,control,mode = PIO_MODE,temp; + char **real_buffer; + register int i; #ifdef DEBUG_READWRITE printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); #endif /* DEBUG_READWRITE */ control = xd_info[drive].control; + if (!xd_dma_buffer) + xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); while (count) { temp = count < xd_maxsectors ? count : xd_maxsectors; @@ -362,10 +466,18 @@ printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp); #endif /* DEBUG_READWRITE */ - mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)buffer,temp * 0x200); + if (xd_dma_buffer) { + mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200); + real_buffer = &xd_dma_buffer; + for (i=0; i < (temp * 0x200); i++) + xd_dma_buffer[i] = buffer[i]; + } + else + real_buffer = &buffer; + xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control); - switch (xd_command(cmdblk,mode,(u_char *) buffer,(u_char *) buffer,sense,XD_TIMEOUT)) { + switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) { case 1: printk("xd_readwrite: timeout, recalibrating drive\n"); xd_recalibrate(drive); @@ -383,6 +495,10 @@ printk(" - no valid disk address\n"); return (0); } + if (xd_dma_buffer) + for (i=0; i < (temp * 0x200); i++) + buffer[i] = xd_dma_buffer[i]; + count -= temp, buffer += temp * 0x200, block += temp; } return (1); @@ -412,28 +528,24 @@ printk("xd_interrupt_handler: unexpected interrupt\n"); } -/* xd_dma: set up the DMA controller for a data transfer */ +/* xd_setup_dma: set up the DMA controller for a data transfer */ static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count) { - if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */ - if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) { + if (nodma) + return (PIO_MODE); + if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) { #ifdef DEBUG_OTHER printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); #endif /* DEBUG_OTHER */ - return (PIO_MODE); - } - disable_dma(xd_dma); - clear_dma_ff(xd_dma); - set_dma_mode(xd_dma,mode); - set_dma_addr(xd_dma,(u_int) buffer); - set_dma_count(xd_dma,count); - - return (DMA_MODE); /* use DMA and INT */ + return (PIO_MODE); } -#ifdef DEBUG_OTHER - printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n"); -#endif /* DEBUG_OTHER */ - return (PIO_MODE); + disable_dma(xd_dma); + clear_dma_ff(xd_dma); + set_dma_mode(xd_dma,mode); + set_dma_addr(xd_dma,(u_int) buffer); + set_dma_count(xd_dma,count); + + return (DMA_MODE); /* use DMA and INT */ } /* xd_build: put stuff into an array in a format suitable for the controller */ @@ -445,19 +557,57 @@ cmdblk[3] = cylinder & 0xFF; cmdblk[4] = count; cmdblk[5] = control; - + return (cmdblk); } +/* xd_wakeup is called from timer interrupt */ +static void xd_wakeup (void) +{ + wake_up(&xdc_wait); +} + +/* xd_wakeup is called from timer interrupt */ +static void xd_watchdog (void) +{ + xd_error = 1; + wake_up(&xd_wait_int); +} + /* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */ static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout) { u_long expiry = jiffies + timeout; + int success; - while (((inb(port) & mask) != flags) && (jiffies < expiry)) - ; - - return (jiffies >= expiry); + xdc_busy = 1; + while ((success = ((inb(port) & mask) != flags)) && (jiffies < expiry)) { + xd_timer.expires = jiffies; + cli(); + add_timer(&xd_timer); + sleep_on(&xdc_wait); + del_timer(&xd_timer); + sti(); + } + xdc_busy = 0; + return (success); +} + +static inline u_int xd_wait_for_IRQ (void) +{ + xd_watchdog_int.expires = jiffies + 8 * HZ; + add_timer(&xd_watchdog_int); + enable_dma(xd_dma); + sleep_on(&xd_wait_int); + del_timer(&xd_watchdog_int); + xdc_busy = 0; + disable_dma(xd_dma); + if (xd_error) { + printk("xd: missed IRQ - command aborted\n"); + xd_error = 0; + return (1); + } + return (0); } /* xd_command: handle all data transfers necessary for a single command */ @@ -481,17 +631,15 @@ switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) { case 0: if (mode == DMA_MODE) { - enable_dma(xd_dma); - sleep_on(&xd_wait_int); - disable_dma(xd_dma); + if (xd_wait_for_IRQ()) + return (1); } else outb(outdata ? *outdata++ : 0,XD_DATA); break; case STAT_INPUT: if (mode == DMA_MODE) { - enable_dma(xd_dma); - sleep_on(&xd_wait_int); - disable_dma(xd_dma); + if (xd_wait_for_IRQ()) + return (1); } else if (indata) *indata++ = inb(XD_DATA); @@ -531,28 +679,93 @@ for (i = 0; i < XD_MAXDRIVES; i++) { xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) { + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); + init_drive(count); count++; + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); } } return (count); } +static void xd_manual_geo_set (u_char drive) +{ + xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]); + xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]); + xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]); +} + static void xd_dtc_init_controller (u_char *address) { switch ((u_long) address) { - case 0xC8000: xd_iobase = 0x320; break; - case 0xCA000: xd_iobase = 0x324; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ + case 0xCA000: xd_iobase = 0x324; + break; + case 0xD0000: /*5150CX*/ + case 0xD8000: break; /*5150CX & 5150XL*/ default: printk("xd_dtc_init_controller: unsupported BIOS address %p\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */ - xd_dma = 3; xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */ outb(0,XD_RESET); /* reset the controller */ } + +static void xd_dtc5150cx_init_drive (u_char drive) +{ + /* values from controller's BIOS - BIOS chip may be removed */ + static u_short geometry_table[][4] = { + {0x200,8,0x200,0x100}, + {0x267,2,0x267,0x267}, + {0x264,4,0x264,0x80}, + {0x132,4,0x132,0x0}, + {0x132,2,0x80, 0x132}, + {0x177,8,0x177,0x0}, + {0x132,8,0x84, 0x0}, + {}, /* not used */ + {0x132,6,0x80, 0x100}, + {0x200,6,0x100,0x100}, + {0x264,2,0x264,0x80}, + {0x280,4,0x280,0x100}, + {0x2B9,3,0x2B9,0x2B9}, + {0x2B9,5,0x2B9,0x2B9}, + {0x280,6,0x280,0x100}, + {0x132,4,0x132,0x0}}; + u_char n; + + n = inb(XD_JUMPER); + n = (drive ? n : (n >> 2)) & 0x33; + n = (n | (n >> 2)) & 0x0F; + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else + if (n != 7) { + xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ + xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ + xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ + xd_info[drive].ecc = 0x0B; /* ecc length */ +#endif /* 0 */ + } + else { + printk("xd%c: undetermined drive geometry\n",'a'+drive); + return; + } + xd_info[drive].control = 5; /* control byte */ + xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); + xd_recalibrate(drive); +} + static void xd_dtc_init_drive (u_char drive) { u_char cmdblk[6],buf[64]; @@ -562,6 +775,8 @@ xd_info[drive].heads = buf[0x0A]; /* heads */ xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */ xd_info[drive].sectors = 17; /* sectors */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); #if 0 xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */ xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */ @@ -581,31 +796,60 @@ static void xd_wd_init_controller (u_char *address) { switch ((u_long) address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xCA000: xd_iobase = 0x324; break; case 0xCC000: xd_iobase = 0x328; break; case 0xCE000: xd_iobase = 0x32C; break; case 0xD0000: xd_iobase = 0x328; break; case 0xD8000: xd_iobase = 0x32C; break; default: printk("xd_wd_init_controller: unsupported BIOS address %p\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* don't know how to auto-detect this yet */ - xd_dma = 3; xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */ - /* outb(0,XD_RESET); */ /* reset the controller */ + outb(0,XD_RESET); /* reset the controller */ + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); } static void xd_wd_init_drive (u_char drive) { + /* values from controller's BIOS - BIOS may be disabled */ + static u_short geometry_table[][4] = { + {0x264,4,0x1C2,0x1C2}, /* common part */ + {0x132,4,0x099,0x0}, + {0x267,2,0x1C2,0x1C2}, + {0x267,4,0x1C2,0x1C2}, + + {0x334,6,0x335,0x335}, /* 1004 series RLL */ + {0x30E,4,0x30F,0x3DC}, + {0x30E,2,0x30F,0x30F}, + {0x267,4,0x268,0x268}, + + {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */ + {0x3DB,7,0x3DC,0x3DC}, + {0x264,4,0x265,0x265}, + {0x267,4,0x268,0x268}}; + u_char cmdblk[6],buf[0x200]; + u_char n = 0,rll,jumper_state,use_jumper_geo; + u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6'); + + jumper_state = ~(inb(0x322)); + if (jumper_state & 0x40) + xd_irq = 9; + rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0; xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0); if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) { xd_info[drive].heads = buf[0x1AF]; /* heads */ xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */ xd_info[drive].sectors = 17; /* sectors */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); #if 0 xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */ xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */ @@ -613,7 +857,43 @@ #endif /* 0 */ xd_info[drive].control = buf[0x1B5]; /* control byte */ - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); + use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders); + if (xd_geo[3*drive]) { + xd_manual_geo_set(drive); + xd_info[drive].control = rll ? 7 : 5; + } + else if (use_jumper_geo) { + n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll; + xd_info[drive].cylinders = geometry_table[n][0]; + xd_info[drive].heads = (u_char)(geometry_table[n][1]); + xd_info[drive].control = rll ? 7 : 5; +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; + xd_info[drive].wprecomp = geometry_table[n][3]; + xd_info[drive].ecc = 0x0B; +#endif /* 0 */ + } + if (!wd_1002) + if (use_jumper_geo) + xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, + geometry_table[n][2],geometry_table[n][3],0x0B); + else + xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, + ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); + /* 1002 based RLL controler requests converted adressing, but reports physical + (physical 26 sec., logical 17 sec.) + 1004 based ???? */ + if (rll & wd_1002) { + if ((xd_info[drive].cylinders *= 26, + xd_info[drive].cylinders /= 17) > 1023) + xd_info[drive].cylinders = 1023; /* 1024 ? */ +#if 0 + xd_info[drive].rwrite *= 26; + xd_info[drive].rwrite /= 17; + xd_info[drive].wprecomp *= 26 + xd_info[drive].wprecomp /= 17; +#endif /* 0 */ + } } else printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive); @@ -622,15 +902,14 @@ static void xd_seagate_init_controller (u_char *address) { switch ((u_long) address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xD0000: xd_iobase = 0x324; break; case 0xD8000: xd_iobase = 0x328; break; case 0xE0000: xd_iobase = 0x32C; break; default: printk("xd_seagate_init_controller: unsupported BIOS address %p\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */ - xd_dma = 3; xd_maxsectors = 0x40; outb(0,XD_RESET); /* reset the controller */ @@ -655,16 +934,15 @@ static void xd_omti_init_controller (u_char *address) { switch ((u_long) address) { - case 0xC8000: xd_iobase = 0x320; break; + case 0x00000: + case 0xC8000: break; /*initial: 0x320 */ case 0xD0000: xd_iobase = 0x324; break; case 0xD8000: xd_iobase = 0x328; break; case 0xE0000: xd_iobase = 0x32C; break; default: printk("xd_omti_init_controller: unsupported BIOS address %p\n",address); - xd_iobase = 0x320; break; + break; } - xd_irq = 5; /* the IRQ and DMA channel are fixed on the Omti controllers */ - xd_dma = 3; xd_maxsectors = 0x40; outb(0,XD_RESET); /* reset the controller */ @@ -679,6 +957,81 @@ xd_info[drive].control = 2; } +/* Xebec support (AK) */ +static void xd_xebec_init_controller (u_char *address) +{ +/* iobase may be set manually in range 0x300 - 0x33C + irq may be set manually to 2(9),3,4,5,6,7 + dma may be set manually to 1,2,3 + (How to detect them ???) +BIOS address may be set manually in range 0x0 - 0xF8000 +If you need non-standard settings use the xd=... command */ + + switch ((u_long) address) { + case 0x00000: + case 0xC8000: /* initially: xd_iobase==0x320 */ + case 0xD0000: + case 0xD2000: + case 0xD4000: + case 0xD6000: + case 0xD8000: + case 0xDA000: + case 0xDC000: + case 0xDE000: + case 0xE0000: break; + default: printk("xd_xebec_init_controller: unsupported BIOS address %p\n",address); + break; + } + + xd_maxsectors = 0x01; + outb(0,XD_RESET); /* reset the controller */ + + xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; + add_timer(&xd_timer); + sleep_on(&xdc_wait); +} + +static void xd_xebec_init_drive (u_char drive) +{ + /* values from controller's BIOS - BIOS chip may be removed */ + static u_short geometry_table[][5] = { + {0x132,4,0x080,0x080,0x7}, + {0x132,4,0x080,0x080,0x17}, + {0x264,2,0x100,0x100,0x7}, + {0x264,2,0x100,0x100,0x17}, + {0x132,8,0x080,0x080,0x7}, + {0x132,8,0x080,0x080,0x17}, + {0x264,4,0x100,0x100,0x6}, + {0x264,4,0x100,0x100,0x17}, + {0x2BC,5,0x2BC,0x12C,0x6}, + {0x3A5,4,0x3A5,0x3A5,0x7}, + {0x26C,6,0x26C,0x26C,0x7}, + {0x200,8,0x200,0x100,0x17}, + {0x400,5,0x400,0x400,0x7}, + {0x400,6,0x400,0x400,0x7}, + {0x264,8,0x264,0x200,0x17}, + {0x33E,7,0x33E,0x200,0x7}}; + u_char n; + + n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry + is assumed for BOTH drives */ + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else { + xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ + xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ + xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ + xd_info[drive].ecc = 0x0B; /* ecc length */ +#endif /* 0 */ + } + xd_info[drive].control = geometry_table[n][4]; /* control byte */ + xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); + xd_recalibrate(drive); +} + /* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */ static void xd_override_init_drive (u_char drive) @@ -686,36 +1039,63 @@ u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 }; u_char cmdblk[6],i; - for (i = 0; i < 3; i++) { - while (min[i] != max[i] - 1) { - test[i] = (min[i] + max[i]) / 2; - xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); - if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) - min[i] = test[i]; - else - max[i] = test[i]; + if (xd_geo[3*drive]) + xd_manual_geo_set(drive); + else { + for (i = 0; i < 3; i++) { + while (min[i] != max[i] - 1) { + test[i] = (min[i] + max[i]) / 2; + xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); + if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + min[i] = test[i]; + else + max[i] = test[i]; + } + test[i] = min[i]; } - test[i] = min[i]; + xd_info[drive].heads = (u_char) min[0] + 1; + xd_info[drive].cylinders = (u_short) min[1] + 1; + xd_info[drive].sectors = (u_char) min[2] + 1; } - xd_info[drive].heads = (u_char) min[0] + 1; - xd_info[drive].cylinders = (u_short) min[1] + 1; - xd_info[drive].sectors = (u_char) min[2] + 1; xd_info[drive].control = 0; } -/* xd_setup: initialise from command line parameters */ +/* xd_setup: initialise controler from command line parameters */ void xd_setup (char *command,int *integers) { - xd_override = 1; - - xd_type = integers[1]; - xd_irq = integers[2]; - xd_iobase = integers[3]; - xd_dma = integers[4]; - + switch (integers[0]) { + case 4: if (integers[4] < 0) + nodma = 1; + else if (integers[4] < 8) + xd_dma = integers[4]; + case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC)) + xd_iobase = integers[3]; + case 2: if ((integers[2] > 0) && (integers[2] < 16)) + xd_irq = integers[2]; + case 1: xd_override = 1; + if ((integers[1] >= 0) && (integers[1] < (sizeof(xd_sigs) / sizeof(xd_sigs[0])))) + xd_type = integers[1]; + case 0: break; + default:printk("xd: too many parameters for xd\n"); + } xd_maxsectors = 0x01; } +#ifndef MODULE +/* xd_manual_geo_init: initialise drive geometry from command line parameters + (used only for WD drives) */ +void xd_manual_geo_init (char *command,int *integers) +{ + int i; + if (integers[0]%3 != 0) { + printk("xd: incorrect number of parameters for xd_geo\n"); + return; + } + for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++) + xd_geo[i] = integers[i+1]; +} +#endif /* MODULE */ + /* xd_setparam: set the drive characteristics */ static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc) { @@ -731,19 +1111,55 @@ cmdblk[12] = (u_char) (wprecomp & 0xFF); cmdblk[13] = ecc; - if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + /* Some controllers require geometry info as data, not command */ + + if (xd_command(cmdblk,PIO_MODE,0,&cmdblk[6],0,XD_TIMEOUT * 2)) printk("xd_setparam: error setting characteristics for drive %d\n",drive); } #ifdef MODULE +static int xd[5] = { -1,-1,-1,-1, }; + +static void xd_done (void) +{ + struct gendisk ** gdp; + + blksize_size[MAJOR_NR] = NULL; + blk_dev[MAJOR_NR].request_fn = NULL; + blk_size[MAJOR_NR] = NULL; + hardsect_size[MAJOR_NR] = NULL; + read_ahead[MAJOR_NR] = 0; + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == &xd_gendisk) + break; + if (*gdp) + *gdp = (*gdp)->next; + release_region(xd_iobase,4); +} + int init_module(void) { + int i,count = 0; int error = xd_init(); + if (!error) { printk(KERN_INFO "XD: Loaded as a module.\n"); + for (i = 4; i > 0; i--) + if(((xd[i] = xd[i-1]) >= 0) && !count) + count = i; + if((xd[0] = count)); + xd_setup(NULL, xd); xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 }); + if (!xd_drives) { + /* no drives detected - unload module */ + unregister_blkdev(MAJOR_NR, "xd"); + xd_done(); + return (-1); + } + for (i = 0; i < xd_drives; i++) + resetup_one_dev(&xd_gendisk, i); } return error; @@ -751,9 +1167,25 @@ void cleanup_module(void) { + int partition,dev,start; + unregister_blkdev(MAJOR_NR, "xd"); - free_irq(xd_irq, NULL); - free_dma(xd_dma); + for (dev = 0; dev < xd_drives; dev++) { + start = dev << xd_gendisk.minor_shift; + for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) { + int minor = (start | partition); + kdev_t devp = MKDEV(MAJOR_NR, minor); + start = dev << xd_gendisk.minor_shift; + sync_dev(devp); + invalidate_buffers(devp); + } + } + xd_done(); + if (xd_drives) { + free_irq(xd_irq, NULL); + free_dma(xd_dma); + if (xd_dma_buffer) + xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); + } } #endif /* MODULE */ - diff -u --recursive --new-file v2.0.33/linux/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- v2.0.33/linux/drivers/cdrom/cdrom.c Sat Aug 17 11:19:26 1996 +++ linux/drivers/cdrom/cdrom.c Wed Jun 3 15:17:47 1998 @@ -97,7 +97,9 @@ /* We need our own cdrom error types! This is a temporary solution. */ +#ifndef ENOMEDIUM #define ENOMEDIUM EAGAIN /* no medium in removable device */ +#endif /* We use the open-option O_NONBLOCK to indicate that the * purpose of opening is only for subsequent ioctl() calls; no device diff -u --recursive --new-file v2.0.33/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.0.33/linux/drivers/char/Config.in Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/Config.in Wed Jun 3 15:17:47 1998 @@ -57,6 +57,10 @@ bool ' Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE bool ' Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK bool ' Power off on shutdown' CONFIG_APM_POWER_OFF + bool ' Ignore multiple suspend' CONFIG_APM_IGNORE_MULTIPLE_SUSPEND +fi +if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then + bool 'Tadpole ANA H8 Support' CONFIG_H8 fi bool 'Watchdog Timer Support' CONFIG_WATCHDOG if [ "$CONFIG_WATCHDOG" != "n" ]; then diff -u --recursive --new-file v2.0.33/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.0.33/linux/drivers/char/Makefile Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/Makefile Wed Jun 3 15:17:47 1998 @@ -196,6 +196,11 @@ M = y endif +ifdef CONFIG_H8 +LX_OBJS += h8.o +M = y +endif + ifdef M LX_OBJS += misc.o else @@ -222,6 +227,9 @@ ifdef CONFIG_TGA_CONSOLE L_OBJS += tga.o + ifdef CONFIG_VGA_CONSOLE + L_OBJS += vga.o vesa_blank.o + endif else ifndef CONFIG_SUN_CONSOLE L_OBJS += vga.o vesa_blank.o diff -u --recursive --new-file v2.0.33/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c --- v2.0.33/linux/drivers/char/apm_bios.c Tue May 14 23:06:55 1996 +++ linux/drivers/char/apm_bios.c Wed Jun 3 15:17:47 1998 @@ -124,6 +124,11 @@ * problems have been reported when using this option with gpm (if you'd * like to debug this, please do so). * + * CONFIG_APM_IGNORE_MULTIPLE_SUSPEND: The IBM TP560 bios seems to insist + * on returning multiple suspend/standby events whenever one occurs. We + * really only need one at a time, so just ignore any beyond the first. + * This is probably safe on most laptops. + * * If you are debugging the APM support for your laptop, note that code for * all of these options is contained in this file, so you can #define or * #undef these on the next line to avoid recompiling the whole kernel. @@ -330,6 +335,9 @@ #endif static int suspends_pending = 0; static int standbys_pending = 0; +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND +static int waiting_for_resume = 0; +#endif static long clock_cmos_diff; static int got_clock_diff = 0; @@ -586,8 +594,15 @@ if (as == sender) continue; as->event_head = (as->event_head + 1) % APM_MAX_EVENTS; - if (as->event_head == as->event_tail) + if (as->event_head == as->event_tail) { + static int notified; + + if (notified == 0) { + printk( "apm_bios: an event queue overflowed\n" ); + notified = 1; + } as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; + } as->events[as->event_head] = event; if (!as->suser) continue; @@ -692,9 +707,23 @@ apm_event_t event; while ((event = get_event()) != 0) { +#ifdef APM_DEBUG + if (event <= NR_APM_EVENT_NAME) + printk("APM BIOS received %s notify\n", + apm_event_name[event - 1]); + else + printk("APM BIOS received unknown event 0x%02x\n", + event); +#endif switch (event) { case APM_SYS_STANDBY: case APM_USER_STANDBY: +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + if (waiting_for_resume) { + return; + } + waiting_for_resume = 1; +#endif send_event(event, APM_STANDBY_RESUME, NULL); if (standbys_pending <= 0) standby(); @@ -707,6 +736,12 @@ break; #endif case APM_SYS_SUSPEND: +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + if (waiting_for_resume) { + return; + } + waiting_for_resume = 1; +#endif send_event(event, APM_NORMAL_RESUME, NULL); if (suspends_pending <= 0) suspend(); @@ -715,6 +750,9 @@ case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: case APM_STANDBY_RESUME: +#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND + waiting_for_resume = 0; +#endif set_time(); send_event(event, 0, NULL); break; @@ -732,14 +770,6 @@ suspend(); break; } -#ifdef APM_DEBUG - if (event <= NR_APM_EVENT_NAME) - printk("APM BIOS received %s notify\n", - apm_event_name[event - 1]); - else - printk("APM BIOS received unknown event 0x%02x\n", - event); -#endif } } diff -u --recursive --new-file v2.0.33/linux/drivers/char/cd1865.h linux/drivers/char/cd1865.h --- v2.0.33/linux/drivers/char/cd1865.h Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/cd1865.h Wed Jun 3 15:17:47 1998 @@ -3,7 +3,7 @@ * for the Specialix IO8+ multiport serial driver. * * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) - * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * Specialix pays for the development and support of this driver. * Please DO contact io8-linux@specialix.co.uk if you require diff -u --recursive --new-file v2.0.33/linux/drivers/char/console.c linux/drivers/char/console.c --- v2.0.33/linux/drivers/char/console.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/char/console.c Wed Jun 3 15:17:47 1998 @@ -594,13 +594,15 @@ __set_origin(__real_origin); } -void scrup(int currcons, unsigned int t, unsigned int b) +static void scrup(int currcons, unsigned int t, unsigned int b, unsigned int nr) { int hardscroll = hardscroll_enabled; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - if (t || b != video_num_lines) + if (t || b != video_num_lines || nr > 1) hardscroll = 0; if (hardscroll) { origin += video_size_row; @@ -641,40 +643,34 @@ set_origin(currcons); } else { unsigned short * d = (unsigned short *) (origin+video_size_row*t); - unsigned short * s = (unsigned short *) (origin+video_size_row*(t+1)); - unsigned int count = (b-t-1) * video_num_columns; + unsigned short * s = (unsigned short *) (origin+video_size_row*(t+nr)); - while (count) { - count--; - scr_writew(scr_readw(s++), d++); - } - count = video_num_columns; - while (count) { - count--; - scr_writew(video_erase_char, d++); - } + memcpyw(d, s, (b-t-nr) * video_size_row); + memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); } } -void -scrdown(int currcons, unsigned int t, unsigned int b) +static void +scrdown(int currcons, unsigned int t, unsigned int b, unsigned int nr) { - unsigned short *d, *s; + unsigned short *s; unsigned int count; + unsigned int step; - if (b > video_num_lines || t >= b) + if (t+nr >= b) + nr = b - t - 1; + if (b > video_num_lines || t >= b || nr < 1) return; - d = (unsigned short *) (origin+video_size_row*b); - s = (unsigned short *) (origin+video_size_row*(b-1)); - count = (b-t-1)*video_num_columns; - while (count) { - count--; - scr_writew(scr_readw(--s), --d); - } - count = video_num_columns; - while (count) { - count--; - scr_writew(video_erase_char, --d); + s = (unsigned short *) (origin+video_size_row*(b-nr-1)); + step = video_num_columns * nr; + count = b - t - nr; + while (count--) { + memcpyw(s + step, s, video_size_row); + s -= video_num_columns; + } + while (nr--) { + s += video_num_columns; + memsetw(s, video_erase_char, video_size_row); } has_scrolled = 1; } @@ -685,7 +681,7 @@ * if below scrolling region */ if (y+1 == bottom) - scrup(currcons,top,bottom); + scrup(currcons,top,bottom, 1); else if (y < video_num_lines-1) { y++; pos += video_size_row; @@ -699,7 +695,7 @@ * if above scrolling region */ if (y == top) - scrdown(currcons,top,bottom); + scrdown(currcons,top,bottom, 1); else if (y > 0) { y--; pos -= video_size_row; @@ -1188,9 +1184,9 @@ need_wrap = 0; } -static void insert_line(int currcons) +static void insert_line(int currcons, unsigned int nr) { - scrdown(currcons,y,bottom); + scrdown(currcons, y, bottom, nr); need_wrap = 0; } @@ -1207,9 +1203,9 @@ need_wrap = 0; } -static void delete_line(int currcons) +static void delete_line(int currcons, unsigned int nr) { - scrup(currcons,y,bottom); + scrup(currcons, y, bottom, nr); need_wrap = 0; } @@ -1229,8 +1225,7 @@ nr = video_num_lines; else if (!nr) nr = 1; - while (nr--) - insert_line(currcons); + insert_line(currcons, nr); } static void csi_P(int currcons, unsigned int nr) @@ -1249,8 +1244,7 @@ nr = video_num_lines; else if (!nr) nr=1; - while (nr--) - delete_line(currcons); + delete_line(currcons, nr); } static void save_cur(int currcons) diff -u --recursive --new-file v2.0.33/linux/drivers/char/h8.c linux/drivers/char/h8.c --- v2.0.33/linux/drivers/char/h8.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/h8.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,1272 @@ +/* + */ +/* + * Hitachi H8/337 Microcontroller driver + * + * The H8 is used to deal with the power and thermal environment + * of a system. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PROC_FS +#include +#include +#endif +#include +#include +#include + +#define __KERNEL_SYSCALLS__ +#include + +#include "h8.h" + +#define DEBUG_H8 + +#ifdef DEBUG_H8 +#define Dprintk printk +#else +#define Dprintk +#endif + +#define XDprintk if(h8_debug==-1)printk + +/* + * The h8 device is one of the misc char devices. + */ +#define H8_MINOR_DEV 140 + +/* + * Forward declarations. + */ +int h8_init(void); +int h8_display_blank(void); +int h8_display_unblank(void); + +static int h8_open(struct inode *, struct file *); +static void h8_release(struct inode *, struct file *); +static long h8_read(struct inode *, struct file *, char *, u_long); +static int h8_select(struct inode *, struct file *, int, select_table *); +static int h8_ioctl(struct inode *, struct file *, u_int, u_long); + +static void h8_intr(int irq, void *dev_id, struct pt_regs *regs); + +#ifdef CONFIG_PROC_FS +static int h8_get_info(char *, char **, off_t, int, int); +#endif + +/* + * Support Routines. + */ +static void h8_hw_init(void); +static void h8_start_new_cmd(void); +static void h8_send_next_cmd_byte(void); +static void h8_read_event_status(void); +static void h8_sync(void); +static void h8_q_cmd(u_char *, int, int); +static void h8_cmd_done(h8_cmd_q_t *qp); +static int h8_alloc_queues(void); + +static u_long h8_get_cpu_speed(void); +static int h8_get_curr_temp(u_char curr_temp[]); +static void h8_get_max_temp(void); +static void h8_get_upper_therm_thold(void); +static void h8_set_upper_therm_thold(int); +static int h8_get_ext_status(u_char stat_word[]); + +static int h8_monitor_thread(void *); + +static int h8_manage_therm(void); +static void h8_set_cpu_speed(int speed_divisor); + +static void h8_start_monitor_timer(unsigned long secs); +static void h8_activate_monitor(unsigned long unused); + +/* in arch/alpha/kernel/lca.c */ +extern void lca_clock_print(void); +extern int lca_get_clock(void); +extern void lca_clock_fiddle(int); + +static void h8_set_event_mask(int); +static void h8_clear_event_mask(int); + +/* + * Driver structures + */ + +static struct timer_list h8_monitor_timer; +static int h8_monitor_timer_active = 0; + +static char driver_version[] = "X0.0";/* no spaces */ + +static struct file_operations h8_fops = { + NULL, /* lseek */ + h8_read, + NULL, /* write */ + NULL, /* readdir */ + h8_select, + h8_ioctl, + NULL, /* mmap */ + h8_open, + h8_release, + NULL, /* fsync */ + NULL /* fasync */ +}; + +static struct miscdevice h8_device = { + H8_MINOR_DEV, + "h8", + &h8_fops +}; + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry h8_proc_entry = { + 0, 3, "h8", S_IFREG | S_IRUGO, 1, 0, 0, 0, 0, h8_get_info +}; +#endif + +union intr_buf intrbuf; +int intr_buf_ptr; +union intr_buf xx; +u_char last_temp; + +/* + * I/O Macros for register reads and writes. + */ +#define H8_READ(a) inb((a)) +#define H8_WRITE(d,a) outb((d),(a)) + +#define H8_GET_STATUS H8_READ((h8_base) + H8_STATUS_REG_OFF) +#define H8_READ_DATA H8_READ((h8_base) + H8_DATA_REG_OFF) +#define WRITE_DATA(d) H8_WRITE((d), h8_base + H8_DATA_REG_OFF) +#define WRITE_CMD(d) H8_WRITE((d), h8_base + H8_CMD_REG_OFF) + +unsigned int h8_base = H8_BASE_ADDR; +unsigned int h8_irq = H8_IRQ; +unsigned int h8_state = H8_IDLE; +unsigned int h8_index = -1; +unsigned int h8_enabled = 0; + +queue_head_t h8_actq, h8_cmdq, h8_freeq; + +/* + * Globals used in thermal control of Alphabook1. + */ +int cpu_speed_divisor = -1; +int h8_event_mask = 0; +struct wait_queue *h8_monitor_wait = NULL; +unsigned int h8_command_mask = 0; +int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD; +int h8_uthermal_window = UTH_HYSTERESIS; +int h8_debug = 0xfffffdfc; +int h8_ldamp = MHZ_115; +int h8_udamp = MHZ_57; +u_char h8_current_temp = 0; +u_char h8_system_temp = 0; +int h8_sync_channel = 0; +struct wait_queue *h8_sync_wait = NULL; +int h8_init_performed; + +/* CPU speeds and clock divisor values */ +int speed_tab[6] = {230, 153, 115, 57, 28, 14}; + +/* + * H8 interrupt handler + */ +static void h8_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + u_char stat_reg, data_reg; + h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link); + + stat_reg = H8_GET_STATUS; + data_reg = H8_READ_DATA; + + XDprintk("h8_intr: state %d status 0x%x data 0x%x\n", h8_state, stat_reg, data_reg); + + switch (h8_state) { + /* Response to an asynchronous event. */ + case H8_IDLE: { /* H8_IDLE */ + if (stat_reg & H8_OFULL) { + if (data_reg == H8_INTR) { + h8_state = H8_INTR_MODE; + /* Executing a command to determine what happened. */ + WRITE_CMD(H8_RD_EVENT_STATUS); + intr_buf_ptr = 1; + WRITE_CMD(H8_RD_EVENT_STATUS); + } else { + Dprintk("h8_intr: idle stat 0x%x data 0x%x\n", + stat_reg, data_reg); + } + } else { + Dprintk("h8_intr: bogus interrupt\n"); + } + break; + } + case H8_INTR_MODE: { /* H8_INTR_MODE */ + XDprintk("H8 intr/intr_mode\n"); + if (data_reg == H8_BYTE_LEVEL_ACK) { + return; + } else if (data_reg == H8_CMD_ACK) { + return; + } else { + intrbuf.byte[intr_buf_ptr] = data_reg; + if(!intr_buf_ptr) { + h8_state = H8_IDLE; + h8_read_event_status(); + } + intr_buf_ptr--; + } + break; + } + /* Placed in this state by h8_start_new_cmd(). */ + case H8_XMIT: { /* H8_XMIT */ + XDprintk("H8 intr/xmit\n"); + /* If a byte level acknowledgement has been received */ + if (data_reg == H8_BYTE_LEVEL_ACK) { + XDprintk("H8 intr/xmit BYTE ACK\n"); + qp->nacks++; + if (qp->nacks > qp->ncmd) + if(h8_debug & 0x1) + Dprintk("h8intr: bogus # of acks!\n"); + /* + * If the number of bytes sent is less than the total + * number of bytes in the command. + */ + if (qp->cnt < qp->ncmd) { + h8_send_next_cmd_byte(); + } + return; + /* If the complete command has produced an acknowledgement. */ + } else if (data_reg == H8_CMD_ACK) { + XDprintk("H8 intr/xmit CMD ACK\n"); + /* If there are response bytes */ + if (qp->nrsp) + h8_state = H8_RCV; + else + h8_state = H8_IDLE; + qp->cnt = 0; + return; + /* Error, need to start over with a clean slate. */ + } else if (data_reg == H8_NACK) { + XDprintk("h8_intr: NACK received restarting command\n"); + qp->nacks = 0; + qp->cnt = 0; + h8_state = H8_IDLE; + WRITE_CMD(H8_SYNC); + return; + } else { + Dprintk ("h8intr: xmit unknown data 0x%x \n", data_reg); + return; + } + break; + } + case H8_RESYNC: { /* H8_RESYNC */ + XDprintk("H8 intr/resync\n"); + if (data_reg == H8_BYTE_LEVEL_ACK) { + return; + } else if (data_reg == H8_SYNC_BYTE) { + h8_state = H8_IDLE; + if (!QUEUE_EMPTY(&h8_actq, link)) + h8_send_next_cmd_byte(); + } else { + Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg); + return; + } + break; + } + case H8_RCV: { /* H8_RCV */ + XDprintk("H8 intr/rcv\n"); + if (qp->cnt < qp->nrsp) { + qp->rcvbuf[qp->cnt] = data_reg; + qp->cnt++; + /* If command reception finished. */ + if (qp->cnt == qp->nrsp) { + h8_state = H8_IDLE; + QUEUE_REMOVE(&h8_actq, qp, link); + h8_cmd_done (qp); + /* More commands to send over? */ + if (!QUEUE_EMPTY(&h8_cmdq, link)) + h8_start_new_cmd(); + } + return; + } else { + Dprintk ("h8intr: rcv overflow cmd 0x%x\n", qp->cmdbuf[0]); + } + break; + } + default: /* default */ + Dprintk("H8 intr/unknown\n"); + break; + } + return; +} + +#ifdef MODULE + +int init_module(void) +{ + printk("H8 module at %X(Interrupt %d)\n", h8_base, h8_irq); + if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL)) + { + printk("H8: error: IRQ %d is not free.\n", h8_irq); + return -EIO; + } + + misc_register(&h8_device); + request_region(h8_base, 8, "h8"); + +#ifdef CONFIG_PROC_FS + proc_register_dynamic(&proc_root, &h8_proc_entry); +#endif + + QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *); + QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *); + QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *); + h8_alloc_queues(); + + h8_hw_init(); + + kernel_thread(h8_monitor_thread, NULL, 0); + + return 0; +} + +void cleanup_module(void) +{ + misc_deregister(&h8_device); + release_region(h8_base, 8); + free_irq(h8_irq, NULL); +} + +#else /* MODULE */ + +int h8_init(void) +{ + if(request_irq(h8_irq, h8_intr, SA_INTERRUPT, "h8", NULL)) + { + printk("H8: error: IRQ %d is not free\n", h8_irq); + return -EIO; + } + printk("H8 at 0x%x IRQ %d\n", h8_base, h8_irq); + +#ifdef CONFIG_PROC_FS + proc_register_dynamic(&proc_root, &h8_proc_entry); +#endif + + misc_register(&h8_device); + request_region(h8_base, 8, "h8"); + + QUEUE_INIT(&h8_actq, link, h8_cmd_q_t *); + QUEUE_INIT(&h8_cmdq, link, h8_cmd_q_t *); + QUEUE_INIT(&h8_freeq, link, h8_cmd_q_t *); + h8_alloc_queues(); + + h8_hw_init(); + + kernel_thread(h8_monitor_thread, NULL, 0); + + return 0; +} +#endif /* MODULE */ + +void h8_hw_init(void) +{ + u_char buf[H8_MAX_CMD_SIZE]; + + /* set CPU speed to max for booting */ + h8_set_cpu_speed(MHZ_230); + + /* + * Initialize the H8 + */ + h8_sync(); /* activate interrupts */ + + /* To clear conditions left by console */ + h8_read_event_status(); + + /* Perform a conditioning read */ + buf[0] = H8_DEVICE_CONTROL; + buf[1] = 0xff; + buf[2] = 0x0; + h8_q_cmd(buf, 3, 1); + + /* Turn on built-in and external mice, capture power switch */ + buf[0] = H8_DEVICE_CONTROL; + buf[1] = 0x0; + buf[2] = H8_ENAB_INT_PTR | H8_ENAB_EXT_PTR | + /*H8_DISAB_PWR_OFF_SW |*/ H8_ENAB_LOW_SPD_IND; + h8_q_cmd(buf, 3, 1); + + h8_enabled = 1; + return; +} + +#ifdef CONFIG_PROC_FS +int h8_get_info(char *buf, char **start, off_t fpos, int length, int dummy) +{ + char *p; + + if (!h8_enabled) + return 0; + p = buf; + + + /* + 0) Linux driver version (this will change if format changes) + 1) + 2) + 3) + 4) + */ + + p += sprintf(p, "%s \n", + driver_version + ); + + return p - buf; +} +#endif + +static long h8_read(struct inode *inode, struct file *fp, char *buf, + u_long count) +{ + printk("h8_read: IMPDEL\n"); + return 0; +} + +static int h8_select(struct inode *inode, struct file *fp, int sel_type, + select_table * wait) +{ + printk("h8_select: IMPDEL\n"); + return 0; +} + +static int h8_ioctl(struct inode * inode, struct file *filp, + u_int cmd, u_long arg) +{ + printk("h8_ioctl: IMPDEL\n"); + return 0; +} + +static void h8_release(struct inode * inode, struct file * filp) +{ + printk("h8_release: IMPDEL\n"); +} + +static int h8_open(struct inode * inode, struct file * filp) +{ + printk("h8_open: IMPDEL\n"); + return 0; +} + +/* Called from console driver -- must make sure h8_enabled. */ +int h8_display_blank(void) +{ +#ifdef CONFIG_H8_DISPLAY_BLANK + int error; + + if (!h8_enabled) + return 0; + error = h8_set_display_power_state(H8_STATE_STANDBY); + if (error == H8_SUCCESS) + return 1; + h8_error("set display standby", error); +#endif + return 0; +} + +/* Called from console driver -- must make sure h8_enabled. */ +int h8_display_unblank(void) +{ +#ifdef CONFIG_H8_DISPLAY_BLANK + int error; + + if (!h8_enabled) + return 0; + error = h8_set_display_power_state(H8_STATE_READY); + if (error == H8_SUCCESS) + return 1; + h8_error("set display ready", error); +#endif + return 0; +} + +int +h8_alloc_queues(void) +{ + h8_cmd_q_t *qp; + unsigned long flags; + int i; + + qp = (h8_cmd_q_t *)kmalloc((sizeof (h8_cmd_q_t) * H8_Q_ALLOC_AMOUNT), + GFP_KERNEL); + + if (!qp) { + printk("H8: could not allocate memory for command queue\n"); + return(0); + } + /* add to the free queue */ + save_flags(flags); cli(); + for (i = 0; i < H8_Q_ALLOC_AMOUNT; i++) { + /* place each at front of freeq */ + QUEUE_ENTER(&h8_freeq, &qp[i], link, h8_cmd_q_t *); + } + restore_flags(flags); + return (1); +} + +/* + * Basic means by which commands are sent to the H8. + */ +void +h8_q_cmd(u_char *cmd, int cmd_size, int resp_size) +{ + h8_cmd_q_t *qp; + unsigned long flags; + int i; + + /* get cmd buf */ + save_flags(flags); cli(); + while (QUEUE_EMPTY(&h8_freeq, link)) { + Dprintk("H8: need to allocate more cmd buffers\n"); + restore_flags(flags); + h8_alloc_queues(); + save_flags(flags); cli(); + } + /* get first element from queue */ + qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_freeq, link); + QUEUE_REMOVE(&h8_freeq, qp, link); + + restore_flags(flags); + + /* fill it in */ + for (i = 0; i < cmd_size; i++) + qp->cmdbuf[i] = cmd[i]; + qp->ncmd = cmd_size; + qp->nrsp = resp_size; + + /* queue it at the end of the cmd queue */ + save_flags(flags); cli(); + + QUEUE_ENTER(&h8_cmdq, qp, link, h8_cmd_q_t *); + + restore_flags(flags); + + h8_start_new_cmd(); +} + +void +h8_start_new_cmd(void) +{ + unsigned long flags; + h8_cmd_q_t *qp; + + save_flags(flags); cli(); + if (h8_state != H8_IDLE) { + if (h8_debug & 0x1) + Dprintk("h8_start_new_cmd: not idle\n"); + restore_flags(flags); + return; + } + + if (!QUEUE_EMPTY(&h8_actq, link)) { + Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n"); + restore_flags(flags); + return; + } + + if (QUEUE_EMPTY(&h8_cmdq, link)) { + Dprintk("h8_start_new_cmd: no command to dequeue\n"); + restore_flags(flags); + return; + } + /* + * Take first command off of the command queue and put + * it on the active queue. + */ + qp = (h8_cmd_q_t *) QUEUE_FIRST(&h8_cmdq, link); + QUEUE_REMOVE(&h8_cmdq, qp, link); + QUEUE_ENTER(&h8_actq, qp, link, h8_cmd_q_t *); + h8_state = H8_XMIT; + if (h8_debug & 0x1) + Dprintk("h8_start_new_cmd: Starting a command\n"); + + qp->cnt = 1; + WRITE_CMD(qp->cmdbuf[0]); /* Kick it off */ + + restore_flags(flags); + return; +} + +void +h8_send_next_cmd_byte(void) +{ + h8_cmd_q_t *qp = (h8_cmd_q_t *)QUEUE_FIRST(&h8_actq, link); + int cnt; + + cnt = qp->cnt; + qp->cnt++; + + if (h8_debug & 0x1) + Dprintk("h8 sending next cmd byte 0x%x (0x%x)\n", + cnt, qp->cmdbuf[cnt]); + + if (cnt) { + WRITE_DATA(qp->cmdbuf[cnt]); + } else { + WRITE_CMD(qp->cmdbuf[cnt]); + } + return; +} + +/* + * Synchronize H8 communications channel for command transmission. + */ +void +h8_sync(void) +{ + u_char buf[H8_MAX_CMD_SIZE]; + + buf[0] = H8_SYNC; + buf[1] = H8_SYNC_BYTE; + h8_q_cmd(buf, 2, 1); +} + +/* + * Responds to external interrupt. Reads event status word and + * decodes type of interrupt. + */ +void +h8_read_event_status(void) +{ + + if(h8_debug & 0x200) + printk("h8_read_event_status: value 0x%x\n", intrbuf.word); + + /* + * Power related items + */ + if (intrbuf.word & H8_DC_CHANGE) { + if(h8_debug & 0x4) + printk("h8_read_event_status: DC_CHANGE\n"); + /* see if dc added or removed, set batt/dc flag, send event */ + + h8_set_event_mask(H8_MANAGE_BATTERY); + wake_up(&h8_monitor_wait); + } + + if (intrbuf.word & H8_POWER_BUTTON) { + printk("Power switch pressed - please wait - preparing to power +off\n"); + h8_set_event_mask(H8_POWER_BUTTON); + wake_up(&h8_monitor_wait); + } + + /* + * Thermal related items + */ + if (intrbuf.word & H8_THERMAL_THRESHOLD) { + if(h8_debug & 0x4) + printk("h8_read_event_status: THERMAL_THRESHOLD\n"); + h8_set_event_mask(H8_MANAGE_UTHERM); + wake_up(&h8_monitor_wait); + } + + /* + * nops -for now + */ + if (intrbuf.word & H8_DOCKING_STATION_STATUS) { + if(h8_debug & 0x4) + printk("h8_read_event_status: DOCKING_STATION_STATUS\n"); + /* read_ext_status */ + } + if (intrbuf.word & H8_EXT_BATT_STATUS) { + if(h8_debug & 0x4) + printk("h8_read_event_status: EXT_BATT_STATUS\n"); + + } + if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) { + if(h8_debug & 0x4) + printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n"); + + } + if (intrbuf.word & H8_BATT_CHANGE_OVER) { + if(h8_debug & 0x4) + printk("h8_read_event_status: BATT_CHANGE_OVER\n"); + + } + if (intrbuf.word & H8_WATCHDOG) { + if(h8_debug & 0x4) + printk("h8_read_event_status: WATCHDOG\n"); + /* nop */ + } + if (intrbuf.word & H8_SHUTDOWN) { + if(h8_debug & 0x4) + printk("h8_read_event_status: SHUTDOWN\n"); + /* nop */ + } + if (intrbuf.word & H8_KEYBOARD) { + if(h8_debug & 0x4) + printk("h8_read_event_status: KEYBOARD\n"); + /* nop */ + } + if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) { + if(h8_debug & 0x4) + printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n"); + /* read_ext_status*/ + } + if (intrbuf.word & H8_INT_BATT_LOW) { + if(h8_debug & 0x4) + printk("h8_read_event_status: INT_BATT_LOW\n"); + /* post event, warn user */ + } + if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) { + if(h8_debug & 0x4) + printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n"); + /* nop - happens often */ + } + if (intrbuf.word & H8_INT_BATT_STATUS) { + if(h8_debug & 0x4) + printk("h8_read_event_status: INT_BATT_STATUS\n"); + + } + if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) { + if(h8_debug & 0x4) + printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n"); + /* nop - happens often */ + } + if (intrbuf.word & H8_EXT_BATT_LOW) { + if(h8_debug & 0x4) + printk("h8_read_event_status: EXT_BATT_LOW\n"); + /*if no internal, post event, warn user */ + /* else nop */ + } + + return; +} + +/* + * Function called when H8 has performed requested command. + */ +void +h8_cmd_done(h8_cmd_q_t *qp) +{ + + /* what to do */ + switch (qp->cmdbuf[0]) { + case H8_SYNC: + if (h8_debug & 0x40000) + printk("H8: Sync command done - byte returned was 0x%x\n", + qp->rcvbuf[0]); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_SN: + case H8_RD_ENET_ADDR: + printk("H8: Read ethernet addr - command done - address: %x - %x - %x - %x - %x - %x \n", + qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2], + qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_HW_VER: + case H8_RD_MIC_VER: + case H8_RD_MAX_TEMP: + printk("H8: Max recorded CPU temp %d, Sys temp %d\n", + qp->rcvbuf[0], qp->rcvbuf[1]); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_MIN_TEMP: + printk("H8: Min recorded CPU temp %d, Sys temp %d\n", + qp->rcvbuf[0], qp->rcvbuf[1]); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_CURR_TEMP: + h8_sync_channel |= H8_RD_CURR_TEMP; + xx.byte[0] = qp->rcvbuf[0]; + xx.byte[1] = qp->rcvbuf[1]; + wake_up(&h8_sync_wait); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_SYS_VARIENT: + case H8_RD_PWR_ON_CYCLES: + printk(" H8: RD_PWR_ON_CYCLES command done\n"); + break; + + case H8_RD_PWR_ON_SECS: + printk("H8: RD_PWR_ON_SECS command done\n"); + break; + + case H8_RD_RESET_STATUS: + case H8_RD_PWR_DN_STATUS: + case H8_RD_EVENT_STATUS: + case H8_RD_ROM_CKSM: + case H8_RD_EXT_STATUS: + xx.byte[1] = qp->rcvbuf[0]; + xx.byte[0] = qp->rcvbuf[1]; + h8_sync_channel |= H8_GET_EXT_STATUS; + wake_up(&h8_sync_wait); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_USER_CFG: + case H8_RD_INT_BATT_VOLT: + case H8_RD_DC_INPUT_VOLT: + case H8_RD_HORIZ_PTR_VOLT: + case H8_RD_VERT_PTR_VOLT: + case H8_RD_EEPROM_STATUS: + case H8_RD_ERR_STATUS: + case H8_RD_NEW_BUSY_SPEED: + case H8_RD_CONFIG_INTERFACE: + case H8_RD_INT_BATT_STATUS: + printk("H8: Read int batt status cmd done - returned was %x %x %x\n", + qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]); + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_RD_EXT_BATT_STATUS: + case H8_RD_PWR_UP_STATUS: + case H8_RD_EVENT_STATUS_MASK: + case H8_CTL_EMU_BITPORT: + case H8_DEVICE_CONTROL: + if(h8_debug & 0x20000) { + printk("H8: Device control cmd done - byte returned was 0x%x\n", + qp->rcvbuf[0]); + } + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_CTL_TFT_BRT_DC: + case H8_CTL_WATCHDOG: + case H8_CTL_MIC_PROT: + case H8_CTL_INT_BATT_CHG: + case H8_CTL_EXT_BATT_CHG: + case H8_CTL_MARK_SPACE: + case H8_CTL_MOUSE_SENSITIVITY: + case H8_CTL_DIAG_MODE: + case H8_CTL_IDLE_AND_BUSY_SPDS: + printk("H8: Idle and busy speed command done\n"); + break; + + case H8_CTL_TFT_BRT_BATT: + case H8_CTL_UPPER_TEMP: + if(h8_debug & 0x10) { + XDprintk("H8: ctl upper thermal thresh cmd done - returned was %d\n", + qp->rcvbuf[0]); + } + QUEUE_ENTER(&h8_freeq, qp, link, h8_cmd_q_t *); + break; + + case H8_CTL_LOWER_TEMP: + case H8_CTL_TEMP_CUTOUT: + case H8_CTL_WAKEUP: + case H8_CTL_CHG_THRESHOLD: + case H8_CTL_TURBO_MODE: + case H8_SET_DIAG_STATUS: + case H8_SOFTWARE_RESET: + case H8_RECAL_PTR: + case H8_SET_INT_BATT_PERCENT: + case H8_WRT_CFG_INTERFACE_REG: + case H8_WRT_EVENT_STATUS_MASK: + case H8_ENTER_POST_MODE: + case H8_EXIT_POST_MODE: + case H8_RD_EEPROM: + case H8_WRT_EEPROM: + case H8_WRT_TO_STATUS_DISP: + printk("H8: Write IO status display command done\n"); + break; + + case H8_DEFINE_SPC_CHAR: + case H8_DEFINE_TABLE_STRING_ENTRY: + case H8_PERFORM_EMU_CMD: + case H8_EMU_RD_REG: + case H8_EMU_WRT_REG: + case H8_EMU_RD_RAM: + case H8_EMU_WRT_RAM: + case H8_BQ_RD_REG: + case H8_BQ_WRT_REG: + case H8_PWR_OFF: + printk ("H8: misc command completed\n"); + break; + } + return; +} + +/* + * Retrieve the current cpu temperature and case temperature. Provides + * the feedback for the thermal control algorithm. Synchcronized via + * sleep() for priority so that no other actions in the process will take + * place before the data becomes available. + */ +int +h8_get_curr_temp(u_char curr_temp[]) +{ + u_char buf[H8_MAX_CMD_SIZE]; + unsigned long flags; + + memset(buf, 0, H8_MAX_CMD_SIZE); + buf[0] = H8_RD_CURR_TEMP; + + h8_q_cmd(buf, 1, 2); + + save_flags(flags); cli(); + + while((h8_sync_channel & H8_RD_CURR_TEMP) == 0) + sleep_on(&h8_sync_wait); + + restore_flags(flags); + + h8_sync_channel &= ~H8_RD_CURR_TEMP; + curr_temp[0] = xx.byte[0]; + curr_temp[1] = xx.byte[1]; + xx.word = 0; + + if(h8_debug & 0x8) + printk("H8: curr CPU temp %d, Sys temp %d\n", + curr_temp[0], curr_temp[1]); + return 0; +} + +static void +h8_get_max_temp(void) +{ + u_char buf[H8_MAX_CMD_SIZE]; + + buf[0] = H8_RD_MAX_TEMP; + h8_q_cmd(buf, 1, 2); +} + +/* + * Assigns an upper limit to the value of the H8 thermal interrupt. + * As an example setting a value of 115 F here will cause the + * interrupt to trigger when the cpu temperature reaches 115 F. + */ +static void +h8_set_upper_therm_thold(int thold) +{ + u_char buf[H8_MAX_CMD_SIZE]; + + /* write 0 to reinitialize interrupt */ + buf[0] = H8_CTL_UPPER_TEMP; + buf[1] = 0x0; + buf[2] = 0x0; + h8_q_cmd(buf, 3, 1); + + /* Do it for real */ + buf[0] = H8_CTL_UPPER_TEMP; + buf[1] = 0x0; + buf[2] = thold; + h8_q_cmd(buf, 3, 1); +} + +static void +h8_get_upper_therm_thold(void) +{ + u_char buf[H8_MAX_CMD_SIZE]; + + buf[0] = H8_CTL_UPPER_TEMP; + buf[1] = 0xff; + buf[2] = 0; + h8_q_cmd(buf, 3, 1); +} + +/* + * The external status word contains information on keyboard controller, + * power button, changes in external batt status, change in DC state, + * docking station, etc. General purpose querying use. + */ +int +h8_get_ext_status(u_char stat_word[]) +{ + u_char buf[H8_MAX_CMD_SIZE]; + unsigned long flags; + + memset(buf, 0, H8_MAX_CMD_SIZE); + buf[0] = H8_RD_EXT_STATUS; + + h8_q_cmd(buf, 1, 2); + + save_flags(flags); cli(); + + while((h8_sync_channel & H8_GET_EXT_STATUS) == 0) + sleep_on(&h8_sync_wait); + + restore_flags(flags); + + h8_sync_channel &= ~H8_GET_EXT_STATUS; + stat_word[0] = xx.byte[0]; + stat_word[1] = xx.byte[1]; + xx.word = 0; + + if(h8_debug & 0x8) + printk("H8: curr ext status %x, %x\n", + stat_word[0], stat_word[1]); + + return 0; +} + +/* + * Thread attached to task 0 manages thermal/physcial state of Alphabook. + * When a condition is detected by the interrupt service routine, the + * isr does a wakeup() on h8_monitor_wait. The mask value is then + * screened for the appropriate action. + */ + +int +h8_monitor_thread(void * unused) +{ + u_char curr_temp[2]; + + /* + * Need a logic based safety valve here. During boot when this thread is + * started and the thermal interrupt is not yet initialized this logic + * checks the temperature and acts accordingly. When this path is acted + * upon system boot is painfully slow, however, the priority associated + * with overheating is high enough to warrant this action. + */ + h8_get_curr_temp(curr_temp); + + printk("H8: Initial CPU temp: %d\n", curr_temp[0]); + + if(curr_temp[0] >= h8_uthermal_threshold) { + h8_set_event_mask(H8_MANAGE_UTHERM); + h8_manage_therm(); + } else { + /* + * Arm the upper thermal limit of the H8 so that any temp in + * excess will trigger the thermal control mechanism. + */ + h8_set_upper_therm_thold(h8_uthermal_threshold); + } + + for(;;) { + sleep_on(&h8_monitor_wait); + + if(h8_debug & 0x2) + printk("h8_monitor_thread awakened, mask:%x\n", + h8_event_mask); + + if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) { + h8_manage_therm(); + } + +#if 0 + if (h8_event_mask & H8_POWER_BUTTON) { + h8_system_down(); + } + + /* + * If an external DC supply is removed or added make + * appropriate cpu speed adjustments. + */ + if (h8_event_mask & H8_MANAGE_BATTERY) { + h8_run_level_3_manage(H8_RUN); + h8_clear_event_mask(H8_MANAGE_BATTERY); + } +#endif + } +} + +/* + * Function implements the following policy. When the machine is booted + * the system is set to run at full clock speed. When the upper thermal + * threshold is reached as a result of full clock a damping factor is + * applied to cool off the cpu. The default value is one quarter clock + * (57 Mhz). When as a result of this cooling a temperature lower by + * hmc_uthermal_window is reached, the machine is reset to a higher + * speed, one half clock (115 Mhz). One half clock is maintained until + * the upper thermal threshold is again reached restarting the cycle. + */ + +int +h8_manage_therm(void) +{ + u_char curr_temp[2]; + + if(h8_event_mask & H8_MANAGE_UTHERM) { + /* Upper thermal interrupt received, need to cool down. */ + if(h8_debug & 0x10) + printk("H8: Thermal threshold %d F reached\n", + h8_uthermal_threshold); + h8_set_cpu_speed(h8_udamp); + h8_clear_event_mask(H8_MANAGE_UTHERM); + h8_set_event_mask(H8_MANAGE_LTHERM); + /* Check again in 30 seconds for cpu temperature */ + h8_start_monitor_timer(H8_TIMEOUT_INTERVAL); + } else if (h8_event_mask & H8_MANAGE_LTHERM) { + /* See how cool the system has become as a result + of the reduction in speed. */ + h8_get_curr_temp(curr_temp); + last_temp = curr_temp[0]; + if (curr_temp[0] < (h8_uthermal_threshold - h8_uthermal_window)) + { + /* System cooling has progressed to a point + that the cpu may be speeded up. */ + h8_set_upper_therm_thold(h8_uthermal_threshold); + h8_set_cpu_speed(h8_ldamp); /* adjustable */ + if(h8_debug & 0x10) + printk("H8: CPU cool, applying cpu_divisor: %d \n", + h8_ldamp); + h8_clear_event_mask(H8_MANAGE_LTHERM); + } + else /* Not cool enough yet, check again in 30 seconds. */ + h8_start_monitor_timer(H8_TIMEOUT_INTERVAL); + } else { + + } + return 0; +} + +/* + * Function conditions the value of global_rpb_counter before + * calling the primitive which causes the actual speed change. + */ +void +h8_set_cpu_speed(int speed_divisor) +{ + +#ifdef NOT_YET +/* + * global_rpb_counter is consumed by alpha_delay() in determining just + * how much time to delay. It is necessary that the number of microseconds + * in DELAY(n) be kept consistent over a variety of cpu clock speeds. + * To that end global_rpb_counter is here adjusted. + */ + + switch (speed_divisor) { + case 0: + global_rpb_counter = rpb->rpb_counter * 2L; + break; + case 1: + global_rpb_counter = rpb->rpb_counter * 4L / 3L ; + break; + case 3: + global_rpb_counter = rpb->rpb_counter / 2L; + break; + case 4: + global_rpb_counter = rpb->rpb_counter / 4L; + break; + case 5: + global_rpb_counter = rpb->rpb_counter / 8L; + break; + /* + * This case most commonly needed for cpu_speed_divisor + * of 2 which is the value assigned by the firmware. + */ + default: + global_rpb_counter = rpb->rpb_counter; + break; + } +#endif /* NOT_YET */ + + if(h8_debug & 0x8) + printk("H8: Setting CPU speed to %d MHz\n", + speed_tab[speed_divisor]); + + /* Make the actual speed change */ + lca_clock_fiddle(speed_divisor); +} + +/* + * Gets value stored in rpb representing cpu clock speed and adjusts this + * value based on the current clock speed divisor. + */ +u_long +h8_get_cpu_speed(void) +{ + u_long speed = 0; + u_long counter; + +#ifdef NOT_YET + counter = rpb->rpb_counter / 1000000L; + + switch (alphabook_get_clock()) { + case 0: + speed = counter * 2L; + break; + case 1: + speed = counter * 4L / 3L ; + break; + case 2: + speed = counter; + break; + case 3: + speed = counter / 2L; + break; + case 4: + speed = counter / 4L; + break; + case 5: + speed = counter / 8L; + break; + default: + break; + } + if(h8_debug & 0x8) + printk("H8: CPU speed current setting: %d MHz\n", speed); +#endif /* NOT_YET */ + return speed; +} + +static void +h8_activate_monitor(unsigned long unused) +{ + unsigned long flags; + + save_flags(flags); cli(); + h8_monitor_timer_active = 0; + restore_flags(flags); + + wake_up(&h8_monitor_wait); +} + +static void +h8_start_monitor_timer(unsigned long secs) +{ + unsigned long flags; + + if (h8_monitor_timer_active) + return; + + save_flags(flags); cli(); + h8_monitor_timer_active = 1; + restore_flags(flags); + + init_timer(&h8_monitor_timer); + h8_monitor_timer.function = h8_activate_monitor; + h8_monitor_timer.expires = secs * HZ + jiffies; + add_timer(&h8_monitor_timer); +} + +static void h8_set_event_mask(int mask) +{ + unsigned long flags; + + save_flags(flags); cli(); + h8_event_mask |= mask; + restore_flags(flags); +} + +static void h8_clear_event_mask(int mask) +{ + unsigned long flags; + + save_flags(flags); cli(); + h8_event_mask &= (~mask); + restore_flags(flags); +} diff -u --recursive --new-file v2.0.33/linux/drivers/char/h8.h linux/drivers/char/h8.h --- v2.0.33/linux/drivers/char/h8.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/h8.h Wed Jun 3 15:17:47 1998 @@ -0,0 +1,250 @@ +/* + */ + +#ifndef __H8_H__ +#define __H8_H__ + +/* + * Register address and offsets + */ +#define H8_BASE_ADDR 0x170 /* default */ +#define H8_IRQ 9 /* default */ +#define H8_STATUS_REG_OFF 0x4 +#define H8_CMD_REG_OFF 0x4 +#define H8_DATA_REG_OFF 0x0 + + +/* H8 register bit definitions */ +/* status register */ +#define H8_OFULL 0x1 /* output data register full */ +#define H8_IFULL 0x2 /* input data register full */ +#define H8_CMD 0x8 /* command / not data */ + +#define H8_INTR 0xfa +#define H8_NACK 0xfc +#define H8_BYTE_LEVEL_ACK 0xfd +#define H8_CMD_ACK 0xfe +#define H8_SYNC_BYTE 0x99 + +/* + * H8 command definitions + */ +/* System info commands */ +#define H8_SYNC 0x0 +#define H8_RD_SN 0x1 +#define H8_RD_ENET_ADDR 0x2 +#define H8_RD_HW_VER 0x3 +#define H8_RD_MIC_VER 0x4 +#define H8_RD_MAX_TEMP 0x5 +#define H8_RD_MIN_TEMP 0x6 +#define H8_RD_CURR_TEMP 0x7 +#define H8_RD_SYS_VARIENT 0x8 +#define H8_RD_PWR_ON_CYCLES 0x9 +#define H8_RD_PWR_ON_SECS 0xa +#define H8_RD_RESET_STATUS 0xb +#define H8_RD_PWR_DN_STATUS 0xc +#define H8_RD_EVENT_STATUS 0xd +#define H8_RD_ROM_CKSM 0xe +#define H8_RD_EXT_STATUS 0xf +#define H8_RD_USER_CFG 0x10 +#define H8_RD_INT_BATT_VOLT 0x11 +#define H8_RD_DC_INPUT_VOLT 0x12 +#define H8_RD_HORIZ_PTR_VOLT 0x13 +#define H8_RD_VERT_PTR_VOLT 0x14 +#define H8_RD_EEPROM_STATUS 0x15 +#define H8_RD_ERR_STATUS 0x16 +#define H8_RD_NEW_BUSY_SPEED 0x17 +#define H8_RD_CONFIG_INTERFACE 0x18 +#define H8_RD_INT_BATT_STATUS 0x19 +#define H8_RD_EXT_BATT_STATUS 0x1a +#define H8_RD_PWR_UP_STATUS 0x1b +#define H8_RD_EVENT_STATUS_MASK 0x56 + +/* Read/write/modify commands */ +#define H8_CTL_EMU_BITPORT 0x32 +#define H8_DEVICE_CONTROL 0x21 +#define H8_CTL_TFT_BRT_DC 0x22 +#define H8_CTL_WATCHDOG 0x23 +#define H8_CTL_MIC_PROT 0x24 +#define H8_CTL_INT_BATT_CHG 0x25 +#define H8_CTL_EXT_BATT_CHG 0x26 +#define H8_CTL_MARK_SPACE 0x27 +#define H8_CTL_MOUSE_SENSITIVITY 0x28 +#define H8_CTL_DIAG_MODE 0x29 +#define H8_CTL_IDLE_AND_BUSY_SPDS 0x2a +#define H8_CTL_TFT_BRT_BATT 0x2b +#define H8_CTL_UPPER_TEMP 0x2c +#define H8_CTL_LOWER_TEMP 0x2d +#define H8_CTL_TEMP_CUTOUT 0x2e +#define H8_CTL_WAKEUP 0x2f +#define H8_CTL_CHG_THRESHOLD 0x30 +#define H8_CTL_TURBO_MODE 0x31 +#define H8_SET_DIAG_STATUS 0x40 +#define H8_SOFTWARE_RESET 0x41 +#define H8_RECAL_PTR 0x42 +#define H8_SET_INT_BATT_PERCENT 0x43 +#define H8_WRT_CFG_INTERFACE_REG 0x45 +#define H8_WRT_EVENT_STATUS_MASK 0x57 +#define H8_ENTER_POST_MODE 0x46 +#define H8_EXIT_POST_MODE 0x47 + +/* Block transfer commands */ +#define H8_RD_EEPROM 0x50 +#define H8_WRT_EEPROM 0x51 +#define H8_WRT_TO_STATUS_DISP 0x52 +#define H8_DEFINE_SPC_CHAR 0x53 + +/* Generic commands */ +#define H8_DEFINE_TABLE_STRING_ENTRY 0x60 + +/* Battery control commands */ +#define H8_PERFORM_EMU_CMD 0x70 +#define H8_EMU_RD_REG 0x71 +#define H8_EMU_WRT_REG 0x72 +#define H8_EMU_RD_RAM 0x73 +#define H8_EMU_WRT_RAM 0x74 +#define H8_BQ_RD_REG 0x75 +#define H8_BQ_WRT_REG 0x76 + +/* System admin commands */ +#define H8_PWR_OFF 0x80 + +/* + * H8 command related definitions + */ + +/* device control argument bits */ +#define H8_ENAB_EXTSMI 0x1 +#define H8_DISAB_IRQ 0x2 +#define H8_ENAB_FLASH_WRT 0x4 +#define H8_ENAB_THERM 0x8 +#define H8_ENAB_INT_PTR 0x10 +#define H8_ENAB_LOW_SPD_IND 0x20 +#define H8_ENAB_EXT_PTR 0x40 +#define H8_DISAB_PWR_OFF_SW 0x80 +#define H8_POWER_OFF 0x80 + +/* H8 read event status bits */ +#define H8_DC_CHANGE 0x1 +#define H8_INT_BATT_LOW 0x2 +#define H8_INT_BATT_CHARGE_THRESHOLD 0x4 +#define H8_INT_BATT_CHARGE_STATE 0x8 +#define H8_INT_BATT_STATUS 0x10 +#define H8_EXT_BATT_CHARGE_STATE 0x20 +#define H8_EXT_BATT_LOW 0x40 +#define H8_EXT_BATT_STATUS 0x80 +#define H8_THERMAL_THRESHOLD 0x100 +#define H8_WATCHDOG 0x200 +#define H8_DOCKING_STATION_STATUS 0x400 +#define H8_EXT_MOUSE_OR_CASE_SWITCH 0x800 +#define H8_KEYBOARD 0x1000 +#define H8_BATT_CHANGE_OVER 0x2000 +#define H8_POWER_BUTTON 0x4000 +#define H8_SHUTDOWN 0x8000 + +/* H8 control idle and busy speeds */ +#define H8_SPEED_LOW 0x1 +#define H8_SPEED_MED 0x2 +#define H8_SPEED_HI 0x3 +#define H8_SPEED_LOCKED 0x80 + +#define H8_MAX_CMD_SIZE 18 +#define H8_Q_ALLOC_AMOUNT 10 + +/* H8 state field values */ +#define H8_IDLE 1 +#define H8_XMIT 2 +#define H8_RCV 3 +#define H8_RESYNC 4 +#define H8_INTR_MODE 5 + +/* Mask values for control functions */ +#define UTH_HYSTERESIS 5 +#define DEFAULT_UTHERMAL_THRESHOLD 115 +#define H8_TIMEOUT_INTERVAL 30 +#define H8_RUN 4 + +#define H8_GET_MAX_TEMP 0x1 +#define H8_GET_CURR_TEMP 0x2 +#define H8_GET_UPPR_THRMAL_THOLD 0x4 +#define H8_GET_ETHERNET_ADDR 0x8 +#define H8_SYNC_OP 0x10 +#define H8_SET_UPPR_THRMAL_THOLD 0x20 +#define H8_GET_INT_BATT_STAT 0x40 +#define H8_GET_CPU_SPD 0x80 +#define H8_MANAGE_UTHERM 0x100 +#define H8_MANAGE_LTHERM 0x200 +#define H8_HALT 0x400 +#define H8_CRASH 0x800 +#define H8_GET_EXT_STATUS 0x10000 +#define H8_MANAGE_QUIET 0x20000 +#define H8_MANAGE_SPEEDUP 0x40000 +#define H8_MANAGE_BATTERY 0x80000 +#define H8_SYSTEM_DELAY_TEST 0x100000 +#define H8_POWER_SWITCH_TEST 0x200000 + +/* cpu speeds and clock divisor values */ +#define MHZ_14 5 +#define MHZ_28 4 +#define MHZ_57 3 +#define MHZ_115 2 +#define MHZ_230 0 + +/* + * H8 data + */ +struct h8_data { + u_int ser_num; + u_char ether_add[6]; + u_short hw_ver; + u_short mic_ver; + u_short max_tmp; + u_short min_tmp; + u_short cur_tmp; + u_int sys_var; + u_int pow_on; + u_int pow_on_secs; + u_char reset_status; + u_char pwr_dn_status; + u_short event_status; + u_short rom_cksm; + u_short ext_status; + u_short u_cfg; + u_char ibatt_volt; + u_char dc_volt; + u_char ptr_horiz; + u_char ptr_vert; + u_char eeprom_status; + u_char error_status; + u_char new_busy_speed; + u_char cfg_interface; + u_short int_batt_status; + u_short ext_batt_status; + u_char pow_up_status; + u_char event_status_mask; +}; + + +/* + * H8 command buffers + */ +typedef struct h8_cmd_q { + DLNODE(struct h8_cmd_q) link; /* double linked list */ + int ncmd; /* number of bytes in command */ + int nrsp; /* number of bytes in response */ + int cnt; /* number of bytes sent/received */ + int nacks; /* number of byte level acks */ + u_char cmdbuf[H8_MAX_CMD_SIZE]; /* buffer to store command */ + u_char rcvbuf[H8_MAX_CMD_SIZE]; /* buffer to store response */ +} h8_cmd_q_t; + +typedef struct __queue_head { + DLNODE(struct h8_cmd_q) link; +} queue_head_t; + +union intr_buf { + u_char byte[2]; + u_int word; +}; + +#endif /* __H8_H_ */ diff -u --recursive --new-file v2.0.33/linux/drivers/char/mem.c linux/drivers/char/mem.c --- v2.0.33/linux/drivers/char/mem.c Tue Sep 9 16:06:48 1997 +++ linux/drivers/char/mem.c Wed Jun 3 15:17:47 1998 @@ -333,6 +333,14 @@ { switch (MINOR(inode->i_rdev)) { case 0: + case 1: + case 2: + case 4: + if(securelevel>0) + return -EPERM; + } + switch (MINOR(inode->i_rdev)) { + case 0: filp->f_op = &ram_fops; break; case 1: @@ -392,7 +400,7 @@ #if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \ defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \ defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \ - defined (CONFIG_PCWATCHDOG) || \ + defined (CONFIG_PCWATCHDOG) || defined (CONFIG_H8) || \ defined (CONFIG_APM) || defined (CONFIG_RTC) || defined (CONFIG_SUN_MOUSE) misc_init(); #endif diff -u --recursive --new-file v2.0.33/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.0.33/linux/drivers/char/misc.c Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/misc.c Wed Jun 3 15:17:47 1998 @@ -232,6 +232,9 @@ #ifdef CONFIG_APM apm_bios_init(); #endif +#ifdef CONFIG_H8 + h8_init(); +#endif #ifdef CONFIG_RTC rtc_init(); #endif diff -u --recursive --new-file v2.0.33/linux/drivers/char/psaux.c linux/drivers/char/psaux.c --- v2.0.33/linux/drivers/char/psaux.c Wed Sep 11 07:57:14 1996 +++ linux/drivers/char/psaux.c Wed Jun 3 15:17:47 1998 @@ -561,7 +561,7 @@ #ifdef MODULE int init_module(void) { - return psaux_init(); /*?? Bjorn */ + return psaux_init(); /*?? Bjorn */ } void cleanup_module(void) diff -u --recursive --new-file v2.0.33/linux/drivers/char/random.c linux/drivers/char/random.c --- v2.0.33/linux/drivers/char/random.c Thu Aug 14 10:05:47 1997 +++ linux/drivers/char/random.c Wed Jun 3 15:17:47 1998 @@ -226,6 +226,7 @@ * Eastlake, Steve Crocker, and Jeff Schiller. */ +#include /* CONFIG_RST_COOKIES and CONFIG_SYN_COOKIES */ #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/char/riscom8.c linux/drivers/char/riscom8.c --- v2.0.33/linux/drivers/char/riscom8.c Fri Apr 26 02:12:25 1996 +++ linux/drivers/char/riscom8.c Wed Jun 3 15:17:47 1998 @@ -1,7 +1,7 @@ /* * linux/drivers/char/riscom.c -- RISCom/8 multiport serial driver. * - * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. The RISCom/8 card diff -u --recursive --new-file v2.0.33/linux/drivers/char/riscom8.h linux/drivers/char/riscom8.h --- v2.0.33/linux/drivers/char/riscom8.h Wed Oct 15 15:11:08 1997 +++ linux/drivers/char/riscom8.h Wed Jun 3 15:17:47 1998 @@ -1,7 +1,7 @@ /* * linux/drivers/char/riscom8.h -- RISCom/8 multiport serial driver. * - * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * This code is loosely based on the Linux serial driver, written by * Linus Torvalds, Theodore T'so and others. The RISCom/8 card diff -u --recursive --new-file v2.0.33/linux/drivers/char/rtc.c linux/drivers/char/rtc.c --- v2.0.33/linux/drivers/char/rtc.c Mon May 27 21:39:18 1996 +++ linux/drivers/char/rtc.c Wed Jun 3 15:17:47 1998 @@ -28,9 +28,14 @@ * Based on other minimal char device drivers, like Alan's * watchdog, Ted's random, etc. etc. * + * 1.07 Paul Gortmaker. + * 1.08 Miquel van Smoorenburg: disallow certain things on the + * DEC Alpha as the CMOS clock is also used for other things. + * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup. + * */ -#define RTC_VERSION "1.07" +#define RTC_VERSION "1.09" #define RTC_IRQ 8 /* Can't see this changing soon. */ #define RTC_IO_EXTENT 0x10 /* Only really two ports, but... */ @@ -98,6 +103,13 @@ unsigned long rtc_freq = 0; /* Current periodic IRQ rate */ unsigned long rtc_irq_data = 0; /* our output to the world */ +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + unsigned char days_in_mo[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -106,8 +118,12 @@ * so that there is no possibility of conflicting with the * set_rtc_mmss() call that happens during some timer interrupts. * (See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.) + * + * On Alpha we won't get any interrupts anyway, as they all end up + * in the system timer code. */ +#ifndef __alpha__ static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { /* @@ -128,9 +144,11 @@ add_timer(&rtc_irq_timer); } } +#endif /* * Now all the various file operations that we export. + * They are all useless on Alpha... *sigh*. */ static int rtc_lseek(struct inode *inode, struct file *file, off_t offset, @@ -141,6 +159,9 @@ static int rtc_read(struct inode *inode, struct file *file, char *buf, int count) { +#ifdef __alpha__ + return -EIO; +#else struct wait_queue wait = { current, NULL }; int retval; @@ -182,6 +203,7 @@ remove_wait_queue(&rtc_wait, &wait); return retval; +#endif } static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, @@ -191,6 +213,7 @@ unsigned long flags; switch (cmd) { +#ifndef __alpha__ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ { mask_rtc_irq_bit(RTC_AIE); @@ -238,6 +261,7 @@ set_rtc_irq_bit(RTC_UIE); return 0; } +#endif case RTC_ALM_READ: /* Read the present alarm time */ { /* @@ -342,7 +366,7 @@ min = rtc_tm.tm_min; sec = rtc_tm.tm_sec; - if ((yrs < 1970) || (yrs > 2069)) + if (yrs < 1970) return -EINVAL; leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); @@ -356,16 +380,19 @@ if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; - if (yrs >= 2000) - yrs -= 2000; /* RTC (0, 1, ... 69) */ - else - yrs -= 1900; /* RTC (70, 71, ... 99) */ + if ((yrs -= epoch) > 255) /* They are unsigned */ + return -EINVAL; save_flags(flags); cli(); - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || - RTC_ALWAYS_BCD) - { + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + if (yrs > 169) { + restore_flags(flags); + return -EINVAL; + } + if (yrs >= 100) + yrs -= 100; + BIN_TO_BCD(sec); BIN_TO_BCD(min); BIN_TO_BCD(hrs); @@ -403,6 +430,7 @@ memcpy_tofs((unsigned long*)arg, &rtc_freq, sizeof(unsigned long)); return 0; } +#ifndef __alpha__ case RTC_IRQP_SET: /* Set periodic IRQ rate. */ { int tmp = 0; @@ -439,6 +467,34 @@ restore_flags(flags); return 0; } +#endif +#ifdef __alpha__ + case RTC_EPOCH_READ: /* Read the epoch. */ + { + int retval; + + retval = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long)); + if (retval != 0) + return retval; + + memcpy_tofs((unsigned long*)arg, &epoch, sizeof(unsigned long)); + return 0; + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!suser()) + return -EACCES; + + epoch = arg; + return 0; + } +#endif default: return -EINVAL; } @@ -448,16 +504,18 @@ * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean * up things on a close. + * On Alpha we just open, for we don't mess with interrups anyway. */ static int rtc_open(struct inode *inode, struct file *file) { - +#ifndef __alpha__ if(rtc_status & RTC_IS_OPEN) return -EBUSY; rtc_status |= RTC_IS_OPEN; rtc_irq_data = 0; +#endif return 0; } @@ -469,6 +527,7 @@ * in use, and clear the data. */ +#ifndef __alpha__ unsigned char tmp; unsigned long flags; @@ -489,8 +548,10 @@ rtc_irq_data = 0; rtc_status &= ~RTC_IS_OPEN; +#endif } +#ifndef __alpha__ static int rtc_select(struct inode *inode, struct file *file, int sel_type, select_table *wait) { @@ -501,6 +562,7 @@ } return 0; } +#endif /* * The various file operations we support. @@ -511,7 +573,11 @@ rtc_read, NULL, /* No write */ NULL, /* No readdir */ +#ifdef __alpha__ + NULL, /* No select on Alpha */ +#else rtc_select, +#endif rtc_ioctl, NULL, /* No mmap */ rtc_open, @@ -528,17 +594,54 @@ int rtc_init(void) { unsigned long flags; +#ifdef __alpha__ + unsigned int year, ctrl; + unsigned long uip_watchdog; + char *guess = NULL; +#endif printk("Real Time Clock Driver v%s\n", RTC_VERSION); +#ifndef __alpha__ if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL)) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ printk("rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; } +#endif misc_register(&rtc_dev); /* Check region? Naaah! Just snarf it up. */ request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc"); +#ifdef __alpha__ + rtc_freq = HZ; + + /* Each operating system on an Alpha uses its own epoch. + Let's try to guess which one we are using now. */ + + uip_watchdog = jiffies; + if (rtc_is_updating() != 0) + while (jiffies - uip_watchdog < 2*HZ/100) + barrier(); + + save_flags(flags); + cli(); + year = CMOS_READ(RTC_YEAR); + ctrl = CMOS_READ(RTC_CONTROL); + restore_flags(flags); + + if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(year); /* This should never happen... */ + + if (year > 10 && year < 44) { + epoch = 1980; + guess = "ARC console"; + } else if (year < 96) { + epoch = 1952; + guess = "Digital UNIX"; + } + if (guess) + printk("rtc: %s epoch (%ld) detected\n", guess, epoch); +#else init_timer(&rtc_irq_timer); rtc_irq_timer.function = rtc_dropped_irq; rtc_wait = NULL; @@ -548,6 +651,7 @@ CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT); restore_flags(flags); rtc_freq = 1024; +#endif return 0; } @@ -563,6 +667,7 @@ * for something that requires a steady > 1KHz signal anyways.) */ +#ifndef __alpha__ void rtc_dropped_irq(unsigned long data) { unsigned long flags; @@ -579,6 +684,7 @@ rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ restore_flags(flags); } +#endif /* * Info exported via "/proc/rtc". @@ -723,7 +829,7 @@ * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; */ - if (rtc_tm->tm_year <= 69) + if ((rtc_tm->tm_year += epoch - 1900) <= 69) rtc_tm->tm_year += 100; rtc_tm->tm_mon--; @@ -763,6 +869,8 @@ * We also clear out any old irq data after an ioctl() that * meddles with the interrupt enable/disable bits. */ + +#ifndef __alpha__ void mask_rtc_irq_bit(unsigned char bit) { unsigned char val; @@ -792,4 +900,4 @@ rtc_irq_data = 0; restore_flags(flags); } - +#endif diff -u --recursive --new-file v2.0.33/linux/drivers/char/scc.c linux/drivers/char/scc.c --- v2.0.33/linux/drivers/char/scc.c Tue Aug 12 14:43:42 1997 +++ linux/drivers/char/scc.c Wed Jun 3 15:17:47 1998 @@ -168,6 +168,7 @@ /* ----------------------------------------------------------------------- */ +#include /* CONFIG_INET and CONFIG_SCC_* */ #include #include #include @@ -208,10 +209,6 @@ #include #include -#include -#include -#include -#include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/char/selection.h linux/drivers/char/selection.h --- v2.0.33/linux/drivers/char/selection.h Wed Oct 15 15:22:39 1997 +++ linux/drivers/char/selection.h Wed Jun 3 15:17:47 1998 @@ -61,10 +61,15 @@ /* how to access screen memory */ #include +#include #ifdef CONFIG_TGA_CONSOLE -extern int tga_blitc(unsigned int, unsigned long); +# ifdef CONFIG_VGA_CONSOLE +extern int curr_cons; +extern const struct console_desc cons_devices[]; +# endif + extern unsigned long video_mem_term; /* @@ -83,24 +88,57 @@ * * NOTE also: there's only *TWO* operations: to put/get a character/attribute. * All the others needed by VGA support go away, as Not Applicable for TGA. + * + * NOTE: "(long) addr < 0" tests for an Alpha kernel virtual address; this + * indicates a VC's backing store; otherwise, it's a bus memory address, for + * the VGA's screen memory, so we do the Alpha "swizzle"... :-) */ +#define IS_VIDEO_MEMORY(a) \ + ((unsigned long)(a) - video_mem_base < video_mem_term - video_mem_base) + static inline void scr_writew(unsigned short val, unsigned short * addr) { + if ((long) addr < 0) { /* - * always deposit the char/attr, then see if it was to "screen" mem. + * always deposit the char/attr, then see if it was to + * "screen" mem. * if so, then render the char/attr onto the real screen. */ *addr = val; - if ((unsigned long)addr < video_mem_term && - (unsigned long)addr >= video_mem_base) { - tga_blitc(val, (unsigned long) addr); - } + + /* + * TGA might need the char blitted to the screen, + * but check first, we could be running on a VGA. + */ + if (con_blitc && IS_VIDEO_MEMORY(addr)) + con_blitc(val, (unsigned long) addr); + } else + writew(val, (unsigned long) addr); } static inline unsigned short scr_readw(unsigned short * addr) { + if ((long) addr < 0) return *addr; + return readw((unsigned long) addr); +} + +/* scr_writeb and scr_readb are not expected to be called with a TGA */ +static inline void scr_writeb(unsigned char val, unsigned char * addr) +{ + if ((long) addr < 0) + *addr = val; + else + writeb(val, (unsigned long) addr); +} + +static inline unsigned char scr_readb(unsigned char * addr) +{ + if ((long) addr < 0) + return *addr; + return readb((unsigned long) addr); } + #else /* CONFIG_TGA_CONSOLE */ /* @@ -109,8 +147,6 @@ */ #ifdef __alpha__ - -#include /* * NOTE: "(long) addr < 0" tests for an Alpha kernel virtual address; this diff -u --recursive --new-file v2.0.33/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v2.0.33/linux/drivers/char/serial.c Fri Sep 5 11:21:39 1997 +++ linux/drivers/char/serial.c Wed Jun 3 15:17:47 1998 @@ -1170,6 +1170,7 @@ int quot = 0; unsigned cflag,cval,fcr; int i; + unsigned long flags; if (!info->tty || !info->tty->termios) return; @@ -1208,15 +1209,15 @@ if (quot) { info->MCR |= UART_MCR_DTR; info->MCR_noint |= UART_MCR_DTR; - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); } else { info->MCR &= ~UART_MCR_DTR; info->MCR_noint &= ~UART_MCR_DTR; - cli(); + save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); - sti(); + restore_flags(flags); return; } /* byte size and parity */ @@ -1302,13 +1303,13 @@ UART_LSR_PE | UART_LSR_FE; } } - cli(); + save_flags(flags); cli(); serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ serial_outp(info, UART_FCR, fcr); /* set fcr */ - sti(); + restore_flags(flags); } static void rs_put_char(struct tty_struct *tty, unsigned char ch) diff -u --recursive --new-file v2.0.33/linux/drivers/char/specialix.c linux/drivers/char/specialix.c --- v2.0.33/linux/drivers/char/specialix.c Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/specialix.c Wed Jun 3 15:17:47 1998 @@ -2,7 +2,7 @@ * specialix.c -- specialix IO8+ multiport serial driver. * * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) - * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * Specialix pays for the development and support of this driver. * Please DO contact io8-linux@specialix.co.uk if you require @@ -69,7 +69,7 @@ */ #include - +#include /* CONFIG_SPECIALIX_RTSCTS */ #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/char/specialix_io8.h linux/drivers/char/specialix_io8.h --- v2.0.33/linux/drivers/char/specialix_io8.h Tue Aug 12 13:06:54 1997 +++ linux/drivers/char/specialix_io8.h Wed Jun 3 15:17:47 1998 @@ -3,7 +3,7 @@ * Specialix IO8+ multiport serial driver. * * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) - * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com) * * * Specialix pays for the development and support of this driver. diff -u --recursive --new-file v2.0.33/linux/drivers/char/tga.c linux/drivers/char/tga.c --- v2.0.33/linux/drivers/char/tga.c Mon Jun 3 20:06:37 1996 +++ linux/drivers/char/tga.c Wed Jun 3 15:17:47 1998 @@ -201,7 +201,7 @@ void tga_clear_screen(void); void -set_palette (void) +tga_set_palette (void) { int i, j; @@ -233,7 +233,7 @@ } void -__set_origin(unsigned short offset) +tga_set_origin(unsigned short offset) { /* * should not be called, but if so, do nothing... @@ -244,7 +244,7 @@ * Hide the cursor from view, during blanking, usually... */ void -hide_cursor(void) +tga_hide_cursor(void) { unsigned long flags; save_flags(flags); cli(); @@ -259,7 +259,7 @@ } void -set_cursor(int currcons) +tga_set_cursor(int currcons) { unsigned int idx, xt, yt, row, col; unsigned long flags; @@ -267,8 +267,10 @@ if (currcons != fg_console || console_blanked || vcmode == KD_GRAPHICS) return; +#if 0 if (__real_origin != __origin) - __set_origin(__real_origin); + tga_set_origin(__real_origin); +#endif save_flags(flags); cli(); @@ -300,12 +302,12 @@ } } else - hide_cursor(); + tga_hide_cursor(); restore_flags(flags); } unsigned long -con_type_init(unsigned long kmem_start, const char **display_desc) +tga_init(unsigned long kmem_start, const char **display_desc) { can_do_color = 1; @@ -328,7 +330,7 @@ * the VGA version of set_scrmem() has some direct VGA references. */ void -get_scrmem(int currcons) +tga_get_scrmem(int currcons) { memcpyw((unsigned short *)vc_scrbuf[currcons], (unsigned short *)origin, video_screen_size); @@ -338,7 +340,7 @@ } void -set_scrmem(int currcons, long offset) +tga_set_scrmem(int currcons, long offset) { if (video_mem_term - video_mem_base < offset + video_screen_size) offset = 0; /* strange ... */ @@ -357,7 +359,7 @@ * for now, we will use/allow *only* our built-in font... */ int -set_get_font(char * arg, int set, int ch512) +tga_set_get_font(char * arg, int set, int ch512) { return -EINVAL; } @@ -371,7 +373,7 @@ * for now, we only support the built-in font... */ int -con_adjust_height(unsigned long fontheight) +tga_adjust_height(unsigned long fontheight) { return -EINVAL; } @@ -386,7 +388,7 @@ */ int -set_get_cmap(unsigned char * arg, int set) { +tga_set_get_cmap(unsigned char * arg, int set) { int i; i = verify_area(set ? VERIFY_READ : VERIFY_WRITE, (void *)arg, 16*3); @@ -424,16 +426,16 @@ * dummy routines for the VESA blanking code, which is VGA only, * so we don't have to carry that stuff around for the TGA... */ -void vesa_powerdown(void) +void tga_vesa_powerdown(void) { } -void vesa_blank(void) +void tga_vesa_blank(void) { } -void vesa_unblank(void) +void tga_vesa_unblank(void) { } -void set_vesa_blanking(const unsigned long arg) +void tga_set_vesa_blanking(const unsigned long arg) { } @@ -441,9 +443,13 @@ * video init code, called from within the PCI bus probing code; * when TGA console is configured, at the end of the probing code, * we call here to look for a TGA device, and proceed... + * + * note that this code MUST be called BEFORE console_init/con_init, + * so that either VGA or TGA are set up but not both. we had to move + * console_init to after pci_init in start_kernel to assure this. */ void -tga_console_init(void) +tga_console_find(void) { unsigned char pci_bus, pci_devfn; int status; @@ -477,10 +483,11 @@ tga_init_video(); tga_clear_screen(); - /* - * FINALLY, we can register TGA as console (whew!) - */ - register_console(console_print); +#ifdef CONFIG_VGA_CONSOLE + /* if both are configured, we are using a dispatch table, + so we must set the index */ + curr_cons = 1; +#endif } unsigned char PLLbits[7] = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 }; diff -u --recursive --new-file v2.0.33/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.0.33/linux/drivers/char/tty_io.c Tue Sep 16 09:36:49 1997 +++ linux/drivers/char/tty_io.c Wed Jun 3 15:17:47 1998 @@ -1163,6 +1163,10 @@ } } #endif + + if (tty->driver.close) + tty->driver.close(tty, filp); + /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the @@ -1219,9 +1223,6 @@ * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. */ - - if (tty->driver.close) - tty->driver.close(tty, filp); if (pty_master) { if (--o_tty->count < 0) { diff -u --recursive --new-file v2.0.33/linux/drivers/char/wdt.c linux/drivers/char/wdt.c --- v2.0.33/linux/drivers/char/wdt.c Fri Apr 12 00:33:25 1996 +++ linux/drivers/char/wdt.c Wed Jun 3 15:17:47 1998 @@ -42,8 +42,8 @@ * You must set these - there is no sane way to probe for this board. */ -int io=0x240; -int irq=14; +static int io=0x240; +static int irq=14; #define WD_TIMO (100*60) /* 1 minute */ diff -u --recursive --new-file v2.0.33/linux/drivers/isdn/avmb1/capiutil.c linux/drivers/isdn/avmb1/capiutil.c --- v2.0.33/linux/drivers/isdn/avmb1/capiutil.c Mon Aug 4 17:33:59 1997 +++ linux/drivers/isdn/avmb1/capiutil.c Wed Jun 3 15:17:47 1998 @@ -25,6 +25,7 @@ * Initial revision * */ +#include /* CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON */ #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c --- v2.0.33/linux/drivers/isdn/isdn_net.c Mon Aug 4 17:34:00 1997 +++ linux/drivers/isdn/isdn_net.c Wed Jun 3 15:17:47 1998 @@ -208,9 +208,7 @@ #include #include "isdn_common.h" #include "isdn_net.h" -#ifdef CONFIG_ISDN_PPP #include "isdn_ppp.h" -#endif /* Prototypes */ @@ -822,6 +820,9 @@ addinfo[0] = '\0'; switch (lp->p_encap) { + case ISDN_NET_ENCAP_SYNCPPP: + p = &buf[IPPP_MAX_HEADER]; + break; case ISDN_NET_ENCAP_IPTYP: proto = ntohs(*(unsigned short *) &buf[0]); p = &buf[2]; @@ -1344,6 +1345,7 @@ isdn_net_local *lp = dev->priv; ushort len = 0; + skb->mac.raw = skb->data; switch (lp->p_encap) { case ISDN_NET_ENCAP_ETHER: len = my_eth_header(skb, dev, type, daddr, saddr, plen); @@ -2256,6 +2258,7 @@ p->local.chargeint = cfg->chargeint * HZ; } if (cfg->p_encap != p->local.p_encap) { + /* FIXME: What if there are alias devices too? */ if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { p->dev.hard_header = NULL; #if (LINUX_VERSION_CODE < 0x02010F) @@ -2282,7 +2285,7 @@ p->dev.hard_header_cache = NULL; #endif p->dev.header_cache_update = NULL; - p->dev.flags = IFF_NOARP; + p->dev.flags = IFF_NOARP | IFF_SOFTHEADERS; } } } diff -u --recursive --new-file v2.0.33/linux/drivers/isdn/sc/includes.h linux/drivers/isdn/sc/includes.h --- v2.0.33/linux/drivers/isdn/sc/includes.h Mon Aug 4 17:34:01 1997 +++ linux/drivers/isdn/sc/includes.h Wed Jun 3 15:17:47 1998 @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/net/3c509.c linux/drivers/net/3c509.c --- v2.0.33/linux/drivers/net/3c509.c Fri Sep 5 11:20:20 1997 +++ linux/drivers/net/3c509.c Wed Jun 3 15:17:47 1998 @@ -689,13 +689,13 @@ lp->stats.rx_packets++; continue; } else if (el3_debug) - printk("%s: Couldn't allocate a sk_buff of size %d.\n", + printk(KERN_WARNING "%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } lp->stats.rx_dropped++; outw(RxDiscard, ioaddr + EL3_CMD); while (inw(ioaddr + EL3_STATUS) & 0x1000) - printk(" Waiting for 3c509 to discard packet, status %x.\n", + printk(KERN_DEBUG " Waiting for 3c509 to discard packet, status %x.\n", inw(ioaddr + EL3_STATUS) ); } diff -u --recursive --new-file v2.0.33/linux/drivers/net/3c515.c linux/drivers/net/3c515.c --- v2.0.33/linux/drivers/net/3c515.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/3c515.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,1502 @@ +/* 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux. */ +/* + Written 1997-1998 by Donald Becker. + + 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 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 +*/ + +static char *version = "3c515.c:v0.99 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; +#define CORKSCREW 1 + +/* "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; +/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ +static const mtu = 1500; +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* 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. */ +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 16 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#else +#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +/* Kernel version compatibility functions. */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) + +#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) + +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#elif defined(MODULE) +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +#endif + +/* "Knobs" for adjusting internal parameters. */ +/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ +#define DRIVER_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage + debugging. */ +static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0; + +/* Number of times to check to see if the Tx FIFO has space, used in some + limited cases. */ +#define WAIT_TX_AVAIL 200 + +/* Operational parameter that usually are not changed. */ +#define TX_TIMEOUT 40 /* Time in jiffies before concluding Tx hung */ + +/* The size here is somewhat misleading: the Corkscrew also uses the ISA + aliased registers at +0x400. + */ +#define CORKSCREW_TOTAL_SIZE 0x20 + +#ifdef HAVE_DEVLIST +struct netdev_entry tc515_drv = +{"3c515", tc515_probe, CORKSCREW_TOTAL_SIZE, NULL}; +#endif + +#ifdef DRIVER_DEBUG +int vortex_debug = DRIVER_DEBUG; +#else +int vortex_debug = 1; +#endif + +#define CORKSCREW_ID 10 + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, +3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, +it's not practical to integrate this driver with the other EtherLink drivers. + +II. Board-specific settings + +The Corkscrew has an EEPROM for configuration, but no special settings are +needed for Linux. + +III. Driver operation + +The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" +PCI cards, with the bus master interface extensively modified to work with +the ISA bus. + +The card is capable of full-bus-master transfers with seperate +lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, +DEC Tulip and Intel Speedo3. + +This driver uses a "RX_COPYBREAK" scheme 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 copying cost of copying a frame to a +correctly-sized skbuff. + + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +IV. Notes + +Thanks to Terry Murphy of 3Com for providing documentation and a development +board. + +The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com +project names. I use these names to eliminate confusion -- 3Com product +numbers and names are very similar and often confused. + +The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! +This driver only supports ethernet frames because of the recent MTU limit +of 1.5K, but the changes to support 4.5K are minimal. +*/ + +/* Operational definitions. + These are not used by other compilation units and thus are not + exported in a ".h" file. + + First the windows. There are eight register windows, with the command + and status registers available in each. + */ +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +/* The top five bits written to EL3_CMD are a command, the lower + 11 bits are the parameter, if applicable. + Note that 11 parameters bits was fine for ethernet, but the new chips + can handle FDDI length frames (~4500 octets) and now parameters count + 32-bit 'Dwords' rather than octets. */ + +enum vortex_cmd { + TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, + RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, + UpStall = 6<<11, UpUnstall = (6<<11)+1, + DownStall = (6<<11)+2, DownUnstall = (6<<11)+3, + RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, + FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, + 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,}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; + +/* Bits in the general status register. */ +enum vortex_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, + DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, + DMAInProgress = 1<<11, /* DMA controller is still busy.*/ + CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, + TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { + Wn0IRQ = 0x08, +#if defined(CORKSCREW) + Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ + Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ +#else + Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ + Wn0EepromData = 12, /* Window 0: EEPROM results register. */ +#endif +}; +enum Win0_EEPROM_bits { + EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; +/* EEPROM locations. */ +enum eeprom_offset { + PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3, + EtherLink3ID=7, }; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; +union wn3_config { + int i; + struct w3_config_fields { + unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; + int pad8:8; + unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; + int pad24:7; + } u; +}; + +enum Window4 { + Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ +}; +enum Win4_Media_bits { + Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ + Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ + Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ + Media_LnkBeat = 0x0800, +}; +enum Window7 { /* Window 7: Bus Master control. */ + Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +}; +/* Boomerang-style bus master control registers. Note ISA aliases! */ +enum MasterCtrl { + PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = 0x40c, + TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, +}; + +/* The Rx and Tx descriptor lists. + Caution Alpha hackers: these types are 32 bits! Note also the 8 byte + alignment contraint on tx_ring[] and rx_ring[]. */ +struct boom_rx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; +/* Values for the Rx status entry. */ +enum rx_desc_status { + RxDComplete=0x00008000, RxDError=0x4000, + /* See boomerang_rx() for actual error bits */ +}; + +struct boom_tx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; + +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. */ + 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]; + 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 sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ + struct timer_list timer; /* Media selection timer. */ + int capabilities; /* Adapter capabilities word. */ + int options; /* User-settable misc. driver options. */ + int last_rx_packets; /* For media autoselection. */ + unsigned int available_media:8, /* From Wn3_Options */ + media_override:3, /* Passed-in media type. */ + default_media:3, /* Read from the EEPROM. */ + 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:1, /* Boomerang */ + tx_full:1; +}; + +/* The action to take with a media selection timer tick. + Note that we deviate from the 3Com order by checking 10base2 before AUI. + */ +enum xcvr_types { + XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, + XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8, +}; + +static struct media_table { + char *name; + unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ + mask:8, /* The transceiver-present bit in Wn3_Config.*/ + next:8; /* The media type to try next. */ + short wait; /* Time before we check media status. */ +} media_tbl[] = { + { "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10}, + { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10}, + { "undefined", 0, 0x80, XCVR_10baseT, 10000}, + { "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10}, + { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10}, + { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10}, + { "MII", 0, 0x40, XCVR_10baseT, 3*HZ }, + { "undefined", 0, 0x01, XCVR_10baseT, 10000}, + { "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 product_index, + int options); +static int vortex_probe1(struct device *dev); +static int vortex_open(struct device *dev); +static void vortex_timer(unsigned long arg); +static int vortex_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 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 set_rx_mode(struct device *dev); + + +/* 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,}; + +#ifdef MODULE +static int debug = -1; +/* A list of all installed Vortex devices, for removing the driver module. */ +static struct device *root_vortex_dev = NULL; + +int +init_module(void) +{ + int cards_found; + + if (debug >= 0) + vortex_debug = debug; + if (vortex_debug) + printk(version); + + root_vortex_dev = NULL; + cards_found = vortex_scan(0); + return cards_found ? 0 : -ENODEV; +} + +#else +int tc515_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; +} +#endif /* not MODULE */ + +static int vortex_scan(struct device *dev) +{ + int cards_found = 0; + static int ioaddr = 0x100; + + /* Check all locations on the ISA bus -- evil! */ + for (; ioaddr < 0x400; ioaddr += 0x20) { + int irq; + if (check_region(ioaddr, CORKSCREW_TOTAL_SIZE)) + continue; + /* Check the resource configuration for a matching ioaddr. */ + if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) + continue; + /* Verify by reading the device ID from the EEPROM. */ + { + int timer; + outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + udelay(162); + if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + if (inw(ioaddr + Wn0EepromData) != 0x6d50) + continue; + } + printk("3c515 Resource configuraiton register %#4.4x, DCR %4.4x.\n", + inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); + irq = inw(ioaddr + 0x2002) & 15; + vortex_found_device(dev, ioaddr, irq, CORKSCREW_ID, dev && dev->mem_start + ? dev->mem_start : options[cards_found]); + dev = 0; + cards_found++; + } + + if (vortex_debug) + printk("%d 3c515 cards found.\n", cards_found); + return cards_found; +} + +static struct device *vortex_found_device(struct device *dev, int ioaddr, + int irq, int product_index, + int options) +{ + struct vortex_private *vp; + +#ifdef MODULE + /* Allocate and fill new device structure. */ + int dev_size = sizeof(struct device) + + sizeof(struct vortex_private) + 15; /* Pad for alignment */ + + 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->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0); + dev->init = vortex_probe1; + vp->product_name = "3c515"; + vp->options = options; + if (options >= 0) { + vp->media_override = ((options & 7) == 2) ? 0 : options & 7; + vp->full_duplex = (options & 8) ? 1 : 0; + vp->bus_master = (options & 16) ? 1 : 0; + } else { + vp->media_override = 7; + vp->full_duplex = 0; + vp->bus_master = 0; + } + ether_setup(dev); + 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->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0); + vp = (struct vortex_private *)dev->priv; + vp->product_name = "3c515"; + vp->options = options; + if (options >= 0) { + vp->media_override = ((options & 7) == 2) ? 0 : options & 7; + vp->full_duplex = (options & 8) ? 1 : 0; + vp->bus_master = (options & 16) ? 1 : 0; + } else { + vp->media_override = 7; + vp->full_duplex = 0; + vp->bus_master = 0; + } + + 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; + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ + int i; + + printk("%s: 3Com %s at %#3x,", dev->name, + vp->product_name, ioaddr); + + /* Read the station address from the EEPROM. */ + EL3WINDOW(0); + for (i = 0; i < 0x18; i++) { + short *phys_addr = (short *)dev->dev_addr; + int timer; + outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + udelay(162); + if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + eeprom[i] = inw(ioaddr + Wn0EepromData); + checksum ^= eeprom[i]; + if (i < 3) + phys_addr[i] = htons(eeprom[i]); + } + checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) + printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); + for (i = 0; i < 6; i++) + printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); + if (eeprom[16] == 0x11c7) { /* Corkscrew */ + if (request_dma(dev->dma, "3c515")) { + printk(", DMA %d allocation failed", dev->dma); + dev->dma = 0; + } else + printk(", DMA %d", dev->dma); + } + printk(", IRQ %d\n", dev->irq); + /* Tell them about an invalid IRQ. */ + if (vortex_debug && (dev->irq <= 0 || dev->irq > 15)) + printk(" *** Warning: this IRQ is unlikely to work! ***\n"); + + { + char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; + union wn3_config config; + EL3WINDOW(3); + vp->available_media = inw(ioaddr + Wn3_Options); + config.i = inl(ioaddr + Wn3_Config); + if (vortex_debug > 1) + printk(" Internal config register is %4.4x, transceivers %#x.\n", + config.i, inw(ioaddr + Wn3_Options)); + printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", + 8 << config.u.ram_size, + config.u.ram_width ? "word" : "byte", + ram_split[config.u.ram_split], + config.u.autoselect ? "autoselect/" : "", + media_tbl[config.u.xcvr].name); + dev->if_port = config.u.xcvr; + vp->default_media = config.u.xcvr; + vp->autoselect = config.u.autoselect; + } + if (vp->media_override != 7) { + printk(" Media override to transceiver type %d (%s).\n", + vp->media_override, media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } + + vp->capabilities = eeprom[16]; + vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; + /* Rx is broken at 10mbps, so we always disable it. */ + /* vp->full_bus_master_rx = 0;*/ + vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; + + /* We do a request_region() to register /proc/ioports info. */ + request_region(ioaddr, CORKSCREW_TOTAL_SIZE, vp->product_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; + dev->set_multicast_list = &set_rx_mode; + + return 0; +} + + +static int +vortex_open(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct vortex_private *vp = (struct vortex_private *)dev->priv; + union wn3_config config; + int i; + + /* Before initializing select the active media port. */ + EL3WINDOW(3); + if (vp->full_duplex) + outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ + config.i = inl(ioaddr + Wn3_Config); + + if (vp->media_override != 7) { + if (vortex_debug > 1) + printk("%s: Media override to transceiver %d (%s).\n", + dev->name, vp->media_override, + media_tbl[vp->media_override].name); + dev->if_port = vp->media_override; + } else if (vp->autoselect) { + /* Find first available media type, starting with 100baseTx. */ + dev->if_port = 4; + while (! (vp->available_media & media_tbl[dev->if_port].mask)) + dev->if_port = media_tbl[dev->if_port].next; + + if (vortex_debug > 1) + printk("%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; + + config.u.xcvr = dev->if_port; + outl(config.i, ioaddr + Wn3_Config); + + if (vortex_debug > 1) { + printk("%s: vortex_open() InternalConfig %8.8x.\n", + dev->name, config.i); + } + + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(RxReset, ioaddr + EL3_CMD); + /* Wait a few ticks for the RxReset command to complete. */ + for (i = 20; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + + /* Use the now-standard shared IRQ implementation. */ + if (vp->capabilities == 0x11c7) { + /* Corkscrew: Cannot share ISA resources. */ + if (dev->irq == 0 + || dev->dma == 0 + || request_irq(dev->irq, &vortex_interrupt, 0, + vp->product_name, dev)) + return -EAGAIN; + enable_dma(dev->dma); + set_dma_mode(dev->dma, DMA_MODE_CASCADE); + } else if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, + vp->product_name, dev)) { + return -EAGAIN; + } + + if (vortex_debug > 1) { + EL3WINDOW(4); + printk("%s: vortex_open() irq %d media status %4.4x.\n", + dev->name, dev->irq, inw(ioaddr + Wn4_Media)); + } + + /* Set the station address and mask in window 2 each time opened. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + i); + for (; i < 12; i+=2) + outw(0, ioaddr + i); + + if (dev->if_port == 3) + /* 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); + + /* Switch to the stats window, and clear all stats by reading. */ + outw(StatsDisable, ioaddr + EL3_CMD); + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(ioaddr + i); + inw(ioaddr + 10); + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, ioaddr + Wn4_NetDiag); + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + if (vp->full_bus_master_rx) { /* Boomerang bus master. */ + vp->cur_rx = vp->dirty_rx = 0; + if (vortex_debug > 2) + printk("%s: Filling in the Rx ring.\n", dev->name); + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + if (i < (RX_RING_SIZE - 1)) + vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]); + else + vp->rx_ring[i].next = 0; + vp->rx_ring[i].status = 0; /* Clear complete bit. */ + vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; + 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. */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + vp->rx_ring[i].addr = virt_to_bus(skb->tail); + } + vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ + outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); + } + if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ + vp->cur_tx = vp->dirty_tx = 0; + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ + /* Clear the Tx ring. */ + for (i = 0; i < TX_RING_SIZE; i++) + vp->tx_skbuff[i] = 0; + outl(0, ioaddr + DownListPtr); + } + /* Set reciever mode: presumably accept b-case and phys addr only. */ + set_rx_mode(dev); + outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0), + 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 + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + ioaddr + EL3_CMD); + + MOD_INC_USE_COUNT; + + return 0; +} + +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; + int ok = 0; + + if (vortex_debug > 1) + printk("%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 0: case 4: case 5: /* 10baseT, 100baseTX, 100baseFX */ + if (media_status & Media_LnkBeat) { + ok = 1; + if (vortex_debug > 1) + printk("%s: Media %s has link beat, %x.\n", + dev->name, media_tbl[dev->if_port].name, media_status); + } else if (vortex_debug > 1) + printk("%s: Media %s is has no link beat, %x.\n", + dev->name, media_tbl[dev->if_port].name, media_status); + + break; + default: /* Other media types handled by Tx timeouts. */ + if (vortex_debug > 1) + printk("%s: Media %s is has no indication, %x.\n", + dev->name, media_tbl[dev->if_port].name, media_status); + ok = 1; + } + if ( ! ok) { + union wn3_config config; + + do { + dev->if_port = media_tbl[dev->if_port].next; + } while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); + if (dev->if_port == 8) { /* Go back to default. */ + dev->if_port = vp->default_media; + if (vortex_debug > 1) + printk("%s: Media selection failing, using default %s port.\n", + dev->name, media_tbl[dev->if_port].name); + } else { + if (vortex_debug > 1) + printk("%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); + } + outw((media_status & ~(Media_10TP|Media_SQE)) | + media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + + EL3WINDOW(3); + config.i = inl(ioaddr + Wn3_Config); + config.u.xcvr = dev->if_port; + outl(config.i, ioaddr + Wn3_Config); + + outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD); + } + EL3WINDOW(old_window); + } restore_flags(flags); + if (vortex_debug > 1) + printk("%s: Media selection timer finished, %s.\n", + dev->name, media_tbl[dev->if_port].name); + +#endif /* AUTOMEDIA*/ + return; +} + +static int +vortex_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + int i; + + /* Min. wait before assuming a Tx failed == 400ms. */ + + if (tickssofar < 400*HZ/1000) /* We probably aren't empty. */ + return 1; + printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", + dev->name, inb(ioaddr + TxStatus), + inw(ioaddr + EL3_STATUS)); + /* Slight code bloat to be user friendly. */ + if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) + printk("%s: Transmitter encountered 16 collisions -- network" + " network cable problem?\n", dev->name); +#ifndef final_version + printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n", + vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx); + printk(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), + &vp->tx_ring[0]); + for (i = 0; i < TX_RING_SIZE; i++) { + printk(" %d: %p length %8.8x status %8.8x\n", i, + &vp->tx_ring[i], + vp->tx_ring[i].length, + vp->tx_ring[i].status); + } +#endif + /* Issue TX_RESET and TX_START commands. */ + outw(TxReset, ioaddr + EL3_CMD); + for (i = 20; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + outw(TxEnable, ioaddr + EL3_CMD); + dev->trans_start = jiffies; + /* dev->tbusy = 0;*/ + vp->stats.tx_errors++; + vp->stats.tx_dropped++; + return 0; /* Yes, silently *drop* the packet! */ + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. + If this ever occurs the queue layer is doing something evil! */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ + /* Calculate the next Tx descriptor entry. */ + int entry = vp->cur_tx % TX_RING_SIZE; + struct boom_tx_desc *prev_entry; + unsigned long flags, i; + + if (vp->tx_full) /* No room to transmit with */ + return 1; + if (vp->cur_tx != 0) + prev_entry = &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE]; + else + prev_entry = NULL; + if (vortex_debug > 3) + printk("%s: Trying to send a packet, Tx index %d.\n", + dev->name, vp->cur_tx); + /* vp->tx_full = 1; */ + 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 | 0x80000000; + vp->tx_ring[entry].status = skb->len | 0x80000000; + + save_flags(flags); + cli(); + outw(DownStall, ioaddr + EL3_CMD); + /* Wait for the stall to complete. */ + for (i = 20; i >= 0 ; i--) + if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) + break; + if (prev_entry) + prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); + if (inl(ioaddr + DownListPtr) == 0) { + outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); + queued_packet++; + } + outw(DownUnstall, ioaddr + EL3_CMD); + restore_flags(flags); + + vp->cur_tx++; + if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) + vp->tx_full = 1; + else { /* Clear previous interrupt enable. */ + if (prev_entry) + prev_entry->status &= ~0x80000000; + dev->tbusy = 0; + } + dev->trans_start = jiffies; + return 0; + } + /* 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((int)(skb->data), ioaddr + Wn7_MasterAddr); + outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); + vp->tx_skb = skb; + outw(StartDMADown, ioaddr + EL3_CMD); + /* dev->tbusy will be cleared at the DMADone interrupt. */ + } else { + /* ... and the packet rounded to a doubleword. */ + outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); + dev_kfree_skb (skb, FREE_WRITE); + if (inw(ioaddr + TxFree) > 1536) { + dev->tbusy = 0; + } else + /* 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_kfree_skb (skb, FREE_WRITE); + if (inw(ioaddr + TxFree) > 1536) { + dev->tbusy = 0; + } 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; + + /* Clear the Tx status stack. */ + { + short tx_status; + int i = 4; + + while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { + if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ + if (vortex_debug > 2) + printk("%s: Tx error, status %2.2x.\n", + dev->name, tx_status); + if (tx_status & 0x04) vp->stats.tx_fifo_errors++; + if (tx_status & 0x38) vp->stats.tx_aborted_errors++; + if (tx_status & 0x30) { + int j; + outw(TxReset, ioaddr + EL3_CMD); + for (j = 20; j >= 0 ; j--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + } + outw(TxEnable, ioaddr + EL3_CMD); + } + outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ + } + } + return 0; +} + +/* 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) +{ + /* Use the now-standard shared IRQ implementation. */ + struct device *dev = dev_id; + struct vortex_private *lp; + int ioaddr, status; + int latency; + int i = max_interrupt_work; + + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk("%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + + ioaddr = dev->base_addr; + latency = inb(ioaddr + Timer); + lp = (struct vortex_private *)dev->priv; + + status = inw(ioaddr + EL3_STATUS); + + if (vortex_debug > 4) + printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name, + status, latency); + if ((status & 0xE000) != 0xE000) { + static int donedidthis=0; + /* Some interrupt controllers store a bogus interrupt from boot-time. + Ignore a single early interrupt, but don't hang the machine for + other interrupt problems. */ + if (donedidthis++ > 100) { + printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", + dev->name, status, dev->start); + FREE_IRQ(dev->irq, dev); + } + } + + do { + if (vortex_debug > 5) + printk("%s: In interrupt loop, status %4.4x.\n", + dev->name, status); + if (status & RxComplete) + vortex_rx(dev); + + if (status & TxAvailable) { + if (vortex_debug > 5) + printk(" TX room bit was handled.\n"); + /* There's room in the FIFO for a full-sized packet. */ + outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); + dev->tbusy = 0; + mark_bh(NET_BH); + } + if (status & DownComplete) { + unsigned int dirty_tx = lp->dirty_tx; + + while (lp->cur_tx - dirty_tx > 0) { + int entry = dirty_tx % TX_RING_SIZE; + if (inl(ioaddr + DownListPtr) == + virt_to_bus(&lp->tx_ring[entry])) + break; /* It still hasn't been processed. */ + if (lp->tx_skbuff[entry]) { + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + } + dirty_tx++; + } + lp->dirty_tx = dirty_tx; + outw(AckIntr | DownComplete, ioaddr + EL3_CMD); + if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { + lp->tx_full= 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + } +#ifdef VORTEX_BUS_MASTER + if (status & DMADone) { + outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ + dev->tbusy = 0; + dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */ + mark_bh(NET_BH); + } +#endif + if (status & UpComplete) { + boomerang_rx(dev); + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + } + if (status & (AdapterFailure | RxEarly | StatsFull)) { + /* Handle all uncommon interrupts at once. */ + if (status & RxEarly) { /* Rx early is unused. */ + vortex_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & StatsFull) { /* Empty statistics. */ + static int DoneDidThat = 0; + if (vortex_debug > 4) + printk("%s: Updating stats.\n", dev->name); + update_stats(ioaddr, dev); + /* DEBUG HACK: Disable statistics as an interrupt source. */ + /* This occurs when we have the wrong media type! */ + if (DoneDidThat == 0 && + inw(ioaddr + EL3_STATUS) & StatsFull) { + int win, reg; + printk("%s: Updating stats failed, disabling stats as an" + " interrupt source.\n", dev->name); + for (win = 0; win < 8; win++) { + EL3WINDOW(win); + printk("\n Vortex window %d:", win); + for (reg = 0; reg < 16; reg++) + printk(" %2.2x", inb(ioaddr+reg)); + } + EL3WINDOW(7); + outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure + | UpComplete | DownComplete | TxComplete, + ioaddr + EL3_CMD); + DoneDidThat++; + } + } + if (status & AdapterFailure) { + /* Adapter failure requires Rx reset and reinit. */ + outw(RxReset, ioaddr + EL3_CMD); + /* Set the Rx filter to the current state. */ + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ + outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); + } + } + + if (--i < 0) { + printk("%s: Too much work in interrupt, status %4.4x. " + "Disabling functions (%4.4x).\n", + dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); + /* Disable all pending interrupts. */ + outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); + outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); + break; + } + /* Acknowledge the IRQ. */ + outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + + } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + + if (vortex_debug > 4) + printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status); + + dev->interrupt = 0; + return; +} + +static int +vortex_rx(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + short rx_status; + + if (vortex_debug > 5) + printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while ((rx_status = inw(ioaddr + RxStatus)) > 0) { + if (rx_status & 0x4000) { /* Error, update stats. */ + unsigned char rx_error = inb(ioaddr + RxErrors); + if (vortex_debug > 2) + printk(" Rx error: status %2.2x.\n", rx_error); + vp->stats.rx_errors++; + if (rx_error & 0x01) vp->stats.rx_over_errors++; + if (rx_error & 0x02) vp->stats.rx_length_errors++; + if (rx_error & 0x04) vp->stats.rx_frame_errors++; + if (rx_error & 0x08) vp->stats.rx_crc_errors++; + if (rx_error & 0x10) vp->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + skb = DEV_ALLOC_SKB(pkt_len + 5); + if (vortex_debug > 4) + printk("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); + 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++; + /* Wait a limited time to go to next packet. */ + for (i = 200; i >= 0; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + continue; + } else if (vortex_debug) + printk("%s: Couldn't allocate a sk_buff of size %d.\n", + dev->name, pkt_len); + } + outw(RxDiscard, ioaddr + EL3_CMD); + vp->stats.rx_dropped++; + /* Wait a limited time to skip this packet. */ + for (i = 200; i >= 0; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + } + + return 0; +} + +static int +boomerang_rx(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int entry = vp->cur_rx % RX_RING_SIZE; + int ioaddr = dev->base_addr; + int rx_status; + + if (vortex_debug > 5) + printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", + inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); + while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { + if (rx_status & RxDError) { /* Error, update stats. */ + unsigned char rx_error = rx_status >> 16; + if (vortex_debug > 2) + printk(" Rx error: status %2.2x.\n", rx_error); + vp->stats.rx_errors++; + if (rx_error & 0x01) vp->stats.rx_over_errors++; + if (rx_error & 0x02) vp->stats.rx_length_errors++; + if (rx_error & 0x04) vp->stats.rx_frame_errors++; + if (rx_error & 0x08) vp->stats.rx_crc_errors++; + if (rx_error & 0x10) vp->stats.rx_length_errors++; + } else { + /* The packet length: up to 4.5K!. */ + short pkt_len = rx_status & 0x1fff; + struct sk_buff *skb; + + if (vortex_debug > 4) + printk("Receiving packet size %d status %4.4x.\n", + pkt_len, rx_status); + + /* 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 = dev; + 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), + pkt_len); + rx_copy++; + } else{ + void *temp; + /* Pass up the skbuff already on the Rx ring. */ + skb = vp->rx_skbuff[entry]; + vp->rx_skbuff[entry] = NULL; + temp = skb_put(skb, pkt_len); + /* Remove this checking code for final release. */ + if (bus_to_virt(vp->rx_ring[entry].addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in boomerang_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(vp->rx_ring[entry].addr), + skb->head, temp); + rx_nocopy++; + } +#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; + vp->stats.rx_packets++; + } + entry = (++vp->cur_rx) % RX_RING_SIZE; + } + /* Refill the Rx ring buffers. */ + for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { + 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) + 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_skbuff[entry] = skb; + } + vp->rx_ring[entry].status = 0; /* Clear complete bit. */ + } + return 0; +} + +static int +vortex_close(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (vortex_debug > 1) { + printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", + dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); + printk("%s: vortex close stats: rx_nocopy %d rx_copy %d" + " tx_queued %d.\n", + dev->name, rx_nocopy, rx_copy, queued_packet); + } + + del_timer(&vp->timer); + + /* Turn off statistics ASAP. We update lp->stats below. */ + outw(StatsDisable, ioaddr + EL3_CMD); + + /* Disable the receiver and transmitter. */ + outw(RxDisable, ioaddr + EL3_CMD); + outw(TxDisable, ioaddr + EL3_CMD); + + if (dev->if_port == XCVR_10base2) + /* 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); + + update_stats(ioaddr, dev); + if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ + outl(0, ioaddr + UpListPtr); + for (i = 0; i < RX_RING_SIZE; i++) + if (vp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + vp->rx_skbuff[i]->free = 1; +#endif + dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE); + vp->rx_skbuff[i] = 0; + } + } + if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ + outl(0, ioaddr + DownListPtr); + for (i = 0; i < TX_RING_SIZE; i++) + if (vp->tx_skbuff[i]) { + dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE); + vp->tx_skbuff[i] = 0; + } + } + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +vortex_get_stats(struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + unsigned long flags; + + if (dev->start) { + save_flags(flags); + cli(); + update_stats(dev->base_addr, dev); + restore_flags(flags); + } + return &vp->stats; +} + +/* Update statistics. + Unlike with the EL3 we need not worry about interrupts changing + the window setting from underneath us, but we must still guard + against a race condition with a StatsUpdate interrupt updating the + table. This is done by checking that the ASM (!) code generated uses + atomic updates with '+='. + */ +static void update_stats(int ioaddr, struct device *dev) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + + /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ + /* Switch to the stats window, and read everything. */ + EL3WINDOW(6); + vp->stats.tx_carrier_errors += inb(ioaddr + 0); + vp->stats.tx_heartbeat_errors += inb(ioaddr + 1); + /* Multiple collisions. */ inb(ioaddr + 2); + vp->stats.collisions += inb(ioaddr + 3); + vp->stats.tx_window_errors += inb(ioaddr + 4); + vp->stats.rx_fifo_errors += inb(ioaddr + 5); + vp->stats.tx_packets += inb(ioaddr + 6); + vp->stats.tx_packets += (inb(ioaddr + 9)&0x30) << 4; + /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */ + /* Tx deferrals */ inb(ioaddr + 8); + /* Don't bother with register 9, an extension of registers 6&7. + If we do use the 6&7 values the atomic update assumption above + is invalid. */ + inw(ioaddr + 10); /* Total Rx and Tx octets. */ + inw(ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(ioaddr + 12); + + /* We change back to window 7 (not 1) with the Vortex. */ + EL3WINDOW(7); + return; +} + +/* This new version of set_rx_mode() supports v1.4 kernels. + The Vortex chip has 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) +{ + int ioaddr = dev->base_addr; + short new_mode; + + if (dev->flags & IFF_PROMISC) { + if (vortex_debug > 3) + printk("%s: Setting promiscuous mode.\n", dev->name); + new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; + } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { + new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; + } else + new_mode = SetRxFilter | RxStation | RxBroadcast; + + outw(new_mode, ioaddr + EL3_CMD); +} + +#ifdef MODULE +void +cleanup_module(void) +{ + struct device *next_dev; + + /* 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; + if (root_vortex_dev->dma) + free_dma(root_vortex_dev->dma); + unregister_netdev(root_vortex_dev); + outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD); + release_region(root_vortex_dev->base_addr, CORKSCREW_TOTAL_SIZE); + kfree(root_vortex_dev); + root_vortex_dev = next_dev; + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c515.c" + * c-indent-level: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c --- v2.0.33/linux/drivers/net/3c59x.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/net/3c59x.c Wed Jun 3 15:17:47 1998 @@ -1,6 +1,6 @@ /* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */ /* - Written 1996-1997 by Donald Becker. + Written 1996-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -15,7 +15,7 @@ */ static char *version = -"3c59x.c:v0.46C 10/14/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n"; +"3c59x.c:v0.99E 5/12/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. @@ -26,6 +26,17 @@ /* 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). */ +#ifdef VORTEX_DEBUG +static int vortex_debug = VORTEX_DEBUG; +#else +static int vortex_debug = 1; +#endif + +/* Some values here only for performance evaluation and path-coverage + 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 @@ -33,7 +44,9 @@ 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'. */ +#if YOU_ARE_BRAVER_THAN_ME #define VORTEX_BUS_MASTER +#endif /* A few values that may be tweaked. */ /* Time in jiffies before concluding the transmitter is hung. */ @@ -89,9 +102,15 @@ #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 + 2) +#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 @@ -111,9 +130,10 @@ #define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) #endif -#if (LINUX_VERSION_CODE < 0x20123) +#if LINUX_VERSION_CODE < 0x20138 #define test_and_set_bit(val, addr) set_bit(val, addr) -#else +#endif +#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115) MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver"); MODULE_PARM(debug, "i"); @@ -121,15 +141,11 @@ MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(compaq_ioaddr, "i"); +MODULE_PARM(compaq_irq, "i"); +MODULE_PARM(compaq_prod_id, "i"); #endif -/* "Knobs" for adjusting internal parameters. */ -/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ -#define VORTEX_DEBUG 1 -/* Some values here only for performance evaluation and path-coverage - debugging. */ -static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0; - /* Operational parameter that usually are not changed. */ /* The Vortex size is twice that of the original EtherLinkIII series: the @@ -144,20 +160,19 @@ {"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL}; #endif -#ifdef VORTEX_DEBUG -static int vortex_debug = VORTEX_DEBUG; -#else -static int vortex_debug = 1; -#endif - -/* Set iff a MII transceiver on any interface requires mdio preamble. */ +/* 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, with the EISA ones last. */ +/* Caution! These entries must be consistent. */ static const int product_ids[] = { - 0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0, 0}; + 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", @@ -165,11 +180,9 @@ "3c900 Boomerang 10Mbps/Combo", "3c905 Boomerang 100baseTx", "3c905 Boomerang 100baseT4", - "3c592 EISA 10mbps Demon/Vortex", - "3c597 EISA Fast Demon/Vortex", + "3c905B Cyclone 100baseTx", + "3c575", /* Cardbus Boomerang */ }; -#define DEMON10_INDEX 8 -#define DEMON100_INDEX 9 /* Theory of Operation @@ -283,7 +296,7 @@ /* Bits in the general status register. */ enum vortex_status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + IntLatch = 0x0001, HostError = 0x0002, TxComplete = 0x0004, TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, @@ -323,7 +336,7 @@ struct w3_config_fields { unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; int pad8:8; - unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; + unsigned int ram_split:2, pad18:2, xcvr:4, autoselect:1; int pad24:7; } u; }; @@ -353,13 +366,15 @@ struct boom_rx_desc { u32 next; /* Last entry points to 0. */ s32 status; - u32 addr; /* Up to addr/len possible.. */ - s32 length; /* set high bit to indicate last pair. */ + u32 addr; /* Up to 63 addr/len pairs possible. */ + s32 length; /* Set LAST_FRAG to indicate last pair. */ }; /* Values for the Rx status entry. */ enum rx_desc_status { RxDComplete=0x00008000, RxDError=0x4000, /* See boomerang_rx() for actual error bits */ + IPChksumErr=1<<25, TCPChksumErr=1<<26, UDPChksumErr=1<<27, + IPChksumValid=1<<29, TCPChksumValid=1<<30, UDPChksumValid=1<<31, }; struct boom_tx_desc { @@ -372,9 +387,13 @@ /* Values for the Tx status entry. */ enum tx_desc_status { CRCDisable=0x2000, TxDComplete=0x8000, + AddIPChksum=0x02000000, AddTCPChksum=0x04000000, AddUDPChksum=0x08000000, TxIntrUploaded=0x80000000, /* IRQ when in FIFO, but maybe not sent. */ }; +/* Chip features we care about in vp->capabilities, read from the EEPROM. */ +enum ChipCaps { CapBusMaster=0x20 }; + struct vortex_private { char devname[8]; /* "ethN" string, also for kernel debug. */ const char *product_name; @@ -389,19 +408,28 @@ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ struct enet_statistics 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; + + /* The remainder are related to chip state, mostly media selection. */ + int in_interrupt; struct timer_list timer; /* Media selection timer. */ int options; /* User-settable misc. driver options. */ - int last_rx_packets; /* For media autoselection. */ - unsigned int available_media:8, /* From Wn3_Options */ + unsigned int media_override:3, /* Passed-in media type. */ - default_media:3, /* Read from the EEPROM. */ + 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; - u16 capabilities; /* Adapter capabilities word. */ - u16 info1, info2; /* Software information information. */ - unsigned char phys[2]; /* MII device addresses. */ + u16 status_enable; + u16 available_media; /* From Wn3_Options. */ + u16 capabilities, info1, info2; /* Various, from EEPROM. */ + u16 advertising; /* NWay media advertisement */ + unsigned char phys[2]; /* MII device addresses. */ }; /* The action to take with a media selection timer tick. @@ -409,15 +437,15 @@ */ enum xcvr_types { XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, - XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8, + XCVR_100baseFx, XCVR_MII=6, XCVR_NWAY=8, XCVR_ExtMII=9, XCVR_Default=10, }; static struct media_table { - char *name; - unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ - mask:8, /* The transceiver-present bit in Wn3_Config.*/ - next:8; /* The media type to try next. */ - short wait; /* Time before we check media status. */ + char *name; + unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ + mask:8, /* The transceiver-present bit in Wn3_Config.*/ + next:8; /* The media type to try next. */ + int wait; /* Time before we check media status. */ } media_tbl[] = { { "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10}, { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10}, @@ -425,14 +453,16 @@ { "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10}, { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10}, { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10}, - { "MII", 0, 0x40, XCVR_10baseT, 3*HZ }, + { "MII", 0, 0x41, XCVR_10baseT, 3*HZ }, { "undefined", 0, 0x01, XCVR_10baseT, 10000}, + { "Autonegotiate", 0, 0x41, XCVR_10baseT, 3*HZ}, + { "MII-External", 0, 0x41, XCVR_10baseT, 3*HZ }, { "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 product_index, + int irq, int device_id, int options, int card_idx); static int vortex_probe1(struct device *dev); static int vortex_open(struct device *dev); @@ -480,25 +510,90 @@ /* A list of all installed Vortex devices, for removing the driver module. */ static struct device *root_vortex_dev = NULL; +#ifdef MODULE /* Variables to work-around the Compaq PCI BIOS32 problem. */ -static int compaq_ioaddr = 0, compaq_irq = 0, compaq_prod_id = 0; +static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900; -#ifdef MODULE static int debug = -1; +#ifdef CARDBUS + +#include + +static dev_node_t *vortex_attach(dev_locator_t *loc) +{ + u16 dev_id; + u32 io; + u8 bus, devfn, irq; + struct device *dev; + + 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_DEVICE_ID, &dev_id); + io &= ~3; + dev = vortex_found_device(NULL, io, irq, dev_id, 0, -1); + if (dev) { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + strcpy(node->dev_name, dev->name); + node->major = node->minor = 0; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } + return NULL; +} + +static void vortex_detach(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "vortex_detach(%s)\n", node->dev_name); + for (devp = &root_vortex_dev; *devp; devp = next) { + next = &((struct vortex_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + struct device *dev = *devp; + if (dev->flags & IFF_UP) + vortex_close(dev); + dev->flags &= ~(IFF_UP|IFF_RUNNING); + unregister_netdev(dev); + kfree(dev); + *devp = *next; + kfree(node); + MOD_DEC_USE_COUNT; + } +} + +struct driver_operations vortex_ops = { + "3c59x_cb", vortex_attach, NULL, NULL, vortex_detach +}; + +#endif /* Cardbus support */ + + int init_module(void) { - int cards_found; - if (debug >= 0) vortex_debug = debug; if (vortex_debug) printk(version); root_vortex_dev = NULL; - cards_found = vortex_scan(0); - return cards_found ? 0 : -ENODEV; +#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; + } +#endif } #else @@ -519,7 +614,8 @@ { int cards_found = 0; -#ifndef NO_PCI /* Allow an EISA-only driver. */ + /* Allow an EISA-only driver. */ +#if defined(CONFIG_PCI) || (defined(MODULE) && !defined(NO_PCI)) /* Ideally we would detect all 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 3Com cards @@ -529,11 +625,11 @@ unsigned char pci_bus, pci_device_fn; for (;pci_index < 0xff; pci_index++) { - unsigned char pci_irq_line, pci_latency; - unsigned short pci_command, vendor, device; - unsigned int pci_ioaddr; + u8 pci_latency; + u16 pci_command, new_command, vendor, device; + int irq; + long ioaddr; - int board_index = 0; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) @@ -542,56 +638,75 @@ PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - 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); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + { +#if LINUX_VERSION_CODE >= 0x20155 + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0]; + 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); + ioaddr = pci_ioaddr; + irq = pci_irq_line; +#endif + } /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + ioaddr &= ~3; if (vendor != TCOM_VENDOR_ID) continue; - for (board_index = 0; product_ids[board_index]; board_index++) { - if (device == product_ids[board_index]) - break; - } - if (product_ids[board_index] == 0) { - printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:" - " not configured.\n", device); + if (ioaddr == 0) { + printk(KERN_WARNING " A 3Com network adapter has been found, " + "however it has not been assigned an I/O address.\n" + " You may need to power-cycle the machine for this " + "device to work!\n"); continue; } - if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE)) + + if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) continue; - dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line, - board_index, dev && dev->mem_start + /* Activate the card. */ + 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); + } + + dev = vortex_found_device(dev, ioaddr, irq, + device, dev && dev->mem_start ? dev->mem_start : options[cards_found], cards_found); if (dev) { - /* Get and check the bus-master and latency values. - Some PCI BIOSes fail to set the master-enable bit, and + 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. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk("%s: PCI Master Bit has not been set! " - " Setting...\n", dev->name); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, pci_command); - } + 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; + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency != 248) { - printk("%s: Overriding PCI latency" - " timer (CFLT) setting of %d, new value is 248.\n", - dev->name, pci_latency); + if (pci_latency < new_latency) { + printk(KERN_INFO "%s: Overriding PCI latency" + " timer (CFLT) setting of %d, new value is %d.\n", + dev->name, pci_latency, new_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 248); + PCI_LATENCY_TIMER, new_latency); } dev = 0; cards_found++; @@ -603,23 +718,19 @@ /* Now check all slots of the EISA bus. */ if (EISA_bus) { static int ioaddr = 0x1000; - for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { - int product_id, product_index; + for ( ; ioaddr < 0x9000; ioaddr += 0x1000) { + int device_id; if (check_region(ioaddr, VORTEX_TOTAL_SIZE)) continue; /* Check the standard EISA ID register for an encoded '3Com'. */ if (inw(ioaddr + 0xC80) != 0x6d50) continue; /* Check for a product that we support, 3c59{2,7} any rev. */ - product_id = inw(ioaddr + 0xC82) & 0xF0FF; - if (product_id == 0x7059) /* 597 */ - product_index = DEMON100_INDEX; - else if (product_id == 0x2059) /* 592 */ - product_index = DEMON10_INDEX; - else + device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); + if ((device_id & 0xFF00) != 0x5900) continue; vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12, - product_index, dev && dev->mem_start + device_id, dev && dev->mem_start ? dev->mem_start : options[cards_found], cards_found); dev = 0; @@ -627,34 +738,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_prod_id, + 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++; dev = 0; } +#endif - /* Finally check for a 3c515 on the ISA bus. */ - /* (3c515 support omitted on this version.) */ + /* 3c515 cards are now supported by the 3c515.c driver. */ return cards_found; } static struct device * vortex_found_device(struct device *dev, int ioaddr, int irq, - int product_index, int options, int card_idx) + int device_id, int option, int card_idx) { struct vortex_private *vp; + const char *product_name; + int board_index = 0; + + 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; + } #ifdef MODULE /* Allocate and fill new device structure. */ - int dev_size = sizeof(struct device) + - sizeof(struct vortex_private) + 15; /* Pad for alignment */ + { + int dev_size = sizeof(struct device) + + sizeof(struct vortex_private) + 15; /* Pad for alignment */ - dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); - memset(dev, 0, dev_size); + 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; @@ -662,18 +797,18 @@ dev->base_addr = ioaddr; dev->irq = irq; dev->init = vortex_probe1; - vp->product_name = product_names[product_index]; - vp->options = options; + 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 = (options >= 0 && (options & 0x10) ? 1 : 0); + vp->full_duplex = (option > 0 && (option & 0x10) ? 1 : 0); - if (options >= 0) { - vp->media_override = ((options & 7) == XCVR_10baseTOnly) ? - XCVR_10baseT : options & 7; - vp->bus_master = (options & 16) ? 1 : 0; + 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; @@ -695,12 +830,12 @@ dev->mtu = mtu; vp = (struct vortex_private *)dev->priv; - vp->product_name = product_names[product_index]; - vp->options = options; - if (options >= 0) { - vp->media_override = ((options & 7) == 2) ? 0 : options & 7; - vp->full_duplex = (options & 8) ? 1 : 0; - vp->bus_master = (options & 16) ? 1 : 0; + vp->product_name = product_name; + vp->options = option; + if (option >= 0) { + vp->media_override = ((option & 7) == 2) ? 0 : option & 7; + vp->full_duplex = (option & 8) ? 1 : 0; + vp->bus_master = (option & 16) ? 1 : 0; } else { vp->media_override = 7; vp->full_duplex = 0; @@ -716,63 +851,87 @@ { 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("%s: 3Com %s at %#3x,", dev->name, - vp->product_name, ioaddr); + printk(KERN_INFO "%s: 3Com %s at %#3x,", + dev->name, vp->product_name, ioaddr); /* Read the station address from the EEPROM. */ EL3WINDOW(0); - for (i = 0; i < 0x18; i++) { - u16 *phys_addr = (u16 *)dev->dev_addr; + for (i = 0; i < 0x40; i++) { int timer; +#ifdef CARDBUS + outw(0x230 + i, ioaddr + Wn0EepromCmd); +#else outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); +#endif /* Pause for at least 162 us. for the read to take place. */ - for (timer = 4; timer >= 0; timer--) { + for (timer = 10; timer >= 0; timer--) { udelay(162); if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break; } eeprom[i] = inw(ioaddr + Wn0EepromData); - checksum ^= eeprom[i]; - if (i >= 10 && i < 13) - phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData)); } + for (i = 0; i < 0x18; i++) + checksum ^= eeprom[i]; checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) { /* Grrr, needless incompatible change 3Com. */ + while (i < 0x21) + checksum ^= eeprom[i++]; + checksum = (checksum ^ (checksum >> 8)) & 0xff; + } if (checksum != 0x00) printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); + + for (i = 0; i < 3; i++) + ether_addr[i] = htons(eeprom[i + 10]); for (i = 0; i < 6; i++) printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); printk(", IRQ %d\n", dev->irq); /* Tell them about an invalid IRQ. */ if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS)) - printk(" *** Warning: this IRQ is unlikely to work! ***\n"); + printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n", + dev->irq); + + /* Extract our information from the EEPROM data. */ + vp->info1 = eeprom[13]; + vp->info2 = eeprom[15]; + vp->capabilities = eeprom[16]; + + if (vp->info1 & 0x8000) + vp->full_duplex = 1; { char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; union wn3_config config; EL3WINDOW(3); vp->available_media = inw(ioaddr + Wn3_Options); + if ((vp->available_media & 0xff) == 0) /* Broken 3c916 */ + vp->available_media = 0x40; config.i = inl(ioaddr + Wn3_Config); if (vortex_debug > 1) - printk(" Internal config register is %4.4x, transceivers %#x.\n", - config.i, inw(ioaddr + Wn3_Options)); - printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", + printk(KERN_DEBUG " Internal config register is %4.4x, " + "transceivers %#x.\n", config.i, inw(ioaddr + Wn3_Options)); + printk(KERN_INFO " %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", 8 << config.u.ram_size, config.u.ram_width ? "word" : "byte", ram_split[config.u.ram_split], config.u.autoselect ? "autoselect/" : "", + config.u.xcvr ? "NWay Autonegotiation" : media_tbl[config.u.xcvr].name); - dev->if_port = config.u.xcvr; vp->default_media = config.u.xcvr; vp->autoselect = config.u.autoselect; } + if (vp->media_override != 7) { - printk(" Media override to transceiver type %d (%s).\n", + printk(KERN_INFO " Media override to transceiver type %d (%s).\n", vp->media_override, media_tbl[vp->media_override].name); dev->if_port = vp->media_override; - } + } else + dev->if_port = vp->default_media; if (dev->if_port == XCVR_MII) { int phy, phy_idx = 0; @@ -780,31 +939,34 @@ for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) { int mii_status; mdio_sync(ioaddr, 32); - mii_status = mdio_read(ioaddr, phy, 0); - if (mii_status != 0xffff) { + mii_status = mdio_read(ioaddr, phy, 1); + if (mii_status && mii_status != 0xffff) { vp->phys[phy_idx++] = phy; - printk("%s: MII transceiver found at address %d.\n", - dev->name, 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; } } if (phy_idx == 0) { - printk("%s: ***WARNING*** No MII transceivers found!\n", - dev->name); - vp->phys[0] = 0; + printk(KERN_WARNING" ***WARNING*** No MII transceivers found!\n"); + vp->phys[0] = 24; + } else { + vp->advertising = mdio_read(ioaddr, vp->phys[0], 4); + if (vp->full_duplex) { + /* Only advertise the FD media types. */ + vp->advertising &= 0x015F; + mdio_write(ioaddr, vp->phys[0], 4, vp->advertising); + } } } - vp->info1 = eeprom[13]; - vp->info2 = eeprom[15]; - vp->capabilities = eeprom[16]; - if (vp->capabilities & 0x20) { - vp->full_bus_master_tx = 1; - printk(" Enabling bus-master transmits and %s receives.\n", - (vp->info2 & 1) ? "early" : "whole-frame" ); - vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; + if (vp->capabilities & CapBusMaster) { + vp->full_bus_master_tx = 1; + printk(KERN_INFO" Enabling bus-master transmits and %s receives.\n", + (vp->info2 & 1) ? "early" : "whole-frame" ); + vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; } /* We do a request_region() to register /proc/ioports info. */ @@ -826,86 +988,6 @@ return 0; } - -/* Read and write the MII registers using software-generated serial - MDIO protocol. The maxium data clock rate is 2.5 Mhz. */ -#define mdio_delay() udelay(1) - -#define MDIO_SHIFT_CLK 0x01 -#define MDIO_DIR_WRITE 0x04 -#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) -#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) -#define MDIO_DATA_READ 0x02 -#define MDIO_ENB_IN 0x00 - -static void mdio_sync(int ioaddr, int bits) -{ - int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - - /* Establish sync by sending at least 32 logic ones. */ - while (-- bits >= 0) { - outw(MDIO_DATA_WRITE1, mdio_addr); - mdio_delay(); - outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } -} -static int mdio_read(int 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; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the read command bits out. */ - for (i = 14; i >= 0; i--) { - int dataval = (read_cmd&(1< 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - return retval>>1 & 0xffff; -} - -static void mdio_write(int ioaddr, int phy_id, int location, int value) -{ - int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; - int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - int i; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (write_cmd&(1<= 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - mdio_delay(); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - } - - return; -} static int @@ -922,7 +1004,7 @@ if (vp->media_override != 7) { if (vortex_debug > 1) - printk("%s: Media override to transceiver %d (%s).\n", + printk(KERN_INFO "%s: Media override to transceiver %d (%s).\n", dev->name, vp->media_override, media_tbl[vp->media_override].name); dev->if_port = vp->media_override; @@ -933,7 +1015,7 @@ dev->if_port = media_tbl[dev->if_port].next; if (vortex_debug > 1) - printk("%s: Initial media type %s.\n", + printk(KERN_DEBUG "%s: Initial media type %s.\n", dev->name, media_tbl[dev->if_port].name); init_timer(&vp->timer); @@ -949,8 +1031,6 @@ if (dev->if_port == XCVR_MII) { int mii_reg1, mii_reg5; - /* We cheat here: we know that we are using the 83840 transceiver - which summarizes the FD status in an extended register. */ EL3WINDOW(4); /* Read BMSR (reg1) only to clear old status. */ mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); @@ -961,7 +1041,7 @@ || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */ vp->full_duplex = 1; if (vortex_debug > 1) - printk("%s: MII #%d status %4.4x, link partner capability %4.4x," + printk(KERN_INFO "%s: MII #%d status %4.4x, link partner capability %4.4x," " setting %s-duplex.\n", dev->name, vp->phys[0], mii_reg1, mii_reg5, vp->full_duplex ? "full" : "half"); EL3WINDOW(3); @@ -972,18 +1052,18 @@ (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); if (vortex_debug > 1) { - printk("%s: vortex_open() InternalConfig %8.8x.\n", + printk(KERN_DEBUG "%s: vortex_open() InternalConfig %8.8x.\n", dev->name, config.i); } outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0 ; i--) + for (i = 2000; i >= 0 ; i--) if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; outw(RxReset, ioaddr + EL3_CMD); /* Wait a few ticks for the RxReset command to complete. */ - for (i = 20; i >= 0 ; i--) + for (i = 2000; i >= 0 ; i--) if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; @@ -991,8 +1071,7 @@ #ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ - if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, - vp->product_name, dev)) { + if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } #else @@ -1007,7 +1086,7 @@ if (vortex_debug > 1) { EL3WINDOW(4); - printk("%s: vortex_open() irq %d media status %4.4x.\n", + printk(KERN_DEBUG "%s: vortex_open() irq %d media status %4.4x.\n", dev->name, dev->irq, inw(ioaddr + Wn4_Media)); } @@ -1047,19 +1126,23 @@ outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD); outl(0x0020, ioaddr + PktStatus); if (vortex_debug > 2) - printk("%s: Filling in the Rx ring.\n", dev->name); + 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].status = 0; /* Clear complete bit. */ vp->rx_ring[i].length = PKT_BUF_SZ | LAST_FRAG; - skb = dev_alloc_skb(PKT_BUF_SZ); + 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); +#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. */ outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); @@ -1077,6 +1160,7 @@ set_rx_mode(dev); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + vp->in_interrupt = 0; dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; @@ -1084,16 +1168,16 @@ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */ - outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull|TxComplete| - (vp->full_bus_master_tx ? DownComplete : TxAvailable) | - (vp->full_bus_master_rx ? UpComplete : RxComplete) | - (vp->bus_master ? DMADone : 0), - ioaddr + EL3_CMD); + vp->status_enable = SetStatusEnb | HostError|IntReq|StatsFull|TxComplete| + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0); + 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 - | AdapterFailure | TxComplete + | HostError | TxComplete | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, ioaddr + EL3_CMD); @@ -1112,7 +1196,7 @@ int ok = 0; if (vortex_debug > 1) - printk("%s: Media selection timer tick happened, %s.\n", + printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n", dev->name, media_tbl[dev->if_port].name); save_flags(flags); cli(); { @@ -1125,10 +1209,10 @@ if (media_status & Media_LnkBeat) { ok = 1; if (vortex_debug > 1) - printk("%s: Media %s has link beat, %x.\n", + printk(KERN_DEBUG "%s: Media %s has link beat, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); } else if (vortex_debug > 1) - printk("%s: Media %s is has no link beat, %x.\n", + printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); break; @@ -1137,7 +1221,7 @@ int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); if (vortex_debug > 1) - printk("%s: MII #%d status register is %4.4x, " + 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) @@ -1146,7 +1230,7 @@ } default: /* Other media types handled by Tx timeouts. */ if (vortex_debug > 1) - printk("%s: Media %s is has no indication, %x.\n", + printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n", dev->name, media_tbl[dev->if_port].name, media_status); ok = 1; } @@ -1159,11 +1243,13 @@ if (dev->if_port == XCVR_Default) { /* Go back to default. */ dev->if_port = vp->default_media; if (vortex_debug > 1) - printk("%s: Media selection failing, using default %s port.\n", + printk(KERN_DEBUG "%s: Media selection failing, using default " + "%s port.\n", dev->name, media_tbl[dev->if_port].name); } else { if (vortex_debug > 1) - printk("%s: Media selection failed, now trying %s port.\n", + 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); @@ -1182,7 +1268,7 @@ EL3WINDOW(old_window); } restore_flags(flags); if (vortex_debug > 1) - printk("%s: Media selection timer finished, %s.\n", + printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n", dev->name, media_tbl[dev->if_port].name); #endif /* AUTOMEDIA*/ @@ -1193,84 +1279,59 @@ { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; - int i; + int j; - printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", + printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n", dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS)); /* Slight code bloat to be user friendly. */ if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) - printk("%s: Transmitter encountered 16 collisions --" + printk(KERN_ERR "%s: Transmitter encountered 16 collisions --" " network cable problem?\n", dev->name); if (inw(ioaddr + EL3_STATUS) & IntLatch) { - printk("%s: Interrupt posted but not handled --" + 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); } -#ifndef final_version + outw(TxReset, ioaddr + EL3_CMD); + for (j = 200; j >= 0 ; j--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + +#if ! defined(final_version) && LINUX_VERSION_CODE >= 0x10300 if (vp->full_bus_master_tx) { - printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n", + int i; + printk(KERN_DEBUG " Flags; bus-master %d, full %d; dirty %d " + "current %d.\n", vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx); - printk(" Transmit list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), + printk(KERN_DEBUG " Transmit list %8.8x vs. %p.\n", + inl(ioaddr + DownListPtr), &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]); for (i = 0; i < TX_RING_SIZE; i++) { - printk(" %d: @%p length %8.8x status %8.8x\n", 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); } } -#ifdef notdef - if (vp->full_bus_master_rx) { - printk(" Switching to non-bus-master receives.\n"); - outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull | - (vp->full_bus_master_tx ? DownComplete : TxAvailable) | - RxComplete | (vp->bus_master ? DMADone : 0), - ioaddr + EL3_CMD); - } - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0 ; i--) - if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; -#endif #endif + vp->stats.tx_errors++; if (vp->full_bus_master_tx) { - /* Change 6/25/97 Michael Sievers sieversm@mail.desy.de - The card has been resetted, but the Tx Ring is still full. - Since the card won't know where to resume, 'update' the - Tx Ring. Probably, we'll lose 16 packets this way, these - will be accounted for as 'dropped'. The code to update the - Tx Ring is taken from the Interrupt handler. */ - - unsigned int dirty_tx = vp->dirty_tx; - - if (vortex_debug > 0) - printk("%s: Freeing Tx ring entries:", dev->name); - while (vp->cur_tx - dirty_tx > 0) { - int entry = dirty_tx % TX_RING_SIZE; - if (inl(ioaddr + DownListPtr) == - virt_to_bus(&vp->tx_ring[entry])) - break; /* It still hasn't been processed. */ - if (vp->tx_skbuff[entry]) { - if (vortex_debug > 0) - printk(" %d\n", entry); - dev_kfree_skb(vp->tx_skbuff[entry], FREE_WRITE); - vp->tx_skbuff[entry] = 0; - vp->stats.tx_dropped++; - } if (vortex_debug > 0) - printk(".\n"); - vp->stats.tx_errors++; - dirty_tx++; - } - vp->dirty_tx = dirty_tx; - vp->tx_full= 0; - } else { /* not bus-master, no Tx ring to clear */ - vp->stats.tx_errors++; - vp->stats.tx_dropped++; - } + printk(KERN_DEBUG "%s: Resetting the Tx ring pointer.\n", + dev->name); + if (vp->cur_tx - vp->dirty_tx > 0 && inl(ioaddr + DownListPtr) == 0) + outl(virt_to_bus(&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]), + ioaddr + DownListPtr); + if (vp->tx_full && (vp->cur_tx - vp->dirty_tx <= TX_RING_SIZE - 1)) { + vp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + } + outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); + outw(DownUnstall, ioaddr + EL3_CMD); + } else + vp->stats.tx_dropped++; /* Issue Tx Enable */ outw(TxEnable, ioaddr + EL3_CMD); @@ -1278,35 +1339,105 @@ /* Switch to register set 7 for normal use. */ EL3WINDOW(7); - - /* The TxFreeThreshold has to be set again after a reset! */ - if (vp->full_bus_master_tx) { - outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); - /* This is to be sure that all bus-master Tx features - are correctly re-initialized after a reset, although - the DownListPtr should already be 0 at this point. */ - outl(0, ioaddr + DownListPtr); +} + +/* + * Handle uncommon interrupt sources. This is a seperate routine to minimize + * the cache impact. + */ +static void +vortex_error(struct device *dev, int status) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + int ioaddr = dev->base_addr; + int do_tx_reset = 0; + int i; + + if (status & TxComplete) { /* Really "TxError" for us. */ + unsigned char tx_status = inb(ioaddr + TxStatus); + /* Presumably a tx-timeout. We must merely re-enable. */ + if (vortex_debug > 2 + || (tx_status != 0x88 && vortex_debug > 0)) + printk(KERN_DEBUG"%s: Transmit error, Tx status register %2.2x.\n", + dev->name, tx_status); + if (tx_status & 0x14) vp->stats.tx_fifo_errors++; + if (tx_status & 0x38) vp->stats.tx_aborted_errors++; + outb(0, ioaddr + TxStatus); + if (tx_status & 0x30) + do_tx_reset = 1; + else /* Merely re-enable the transmitter. */ + outw(TxEnable, ioaddr + EL3_CMD); } - /* finally, allow new Transmits */ - dev->tbusy = 0; - /* End of Michael Sievers changes. */ + if (status & RxEarly) { /* Rx early is unused. */ + vortex_rx(dev); + outw(AckIntr | RxEarly, ioaddr + EL3_CMD); + } + if (status & StatsFull) { /* Empty statistics. */ + static int DoneDidThat = 0; + if (vortex_debug > 4) + printk(KERN_DEBUG "%s: Updating stats.\n", dev->name); + update_stats(ioaddr, dev); + /* HACK: Disable statistics as an interrupt source. */ + /* This occurs when we have the wrong media type! */ + if (DoneDidThat == 0 && + inw(ioaddr + EL3_STATUS) & StatsFull) { + printk(KERN_WARNING "%s: Updating statistics failed, disabling " + "stats as an interrupt source.\n", dev->name); + EL3WINDOW(5); + outw(SetIntrEnb | (inw(ioaddr + 10) & ~StatsFull), ioaddr + EL3_CMD); + EL3WINDOW(7); + DoneDidThat++; + } + } + if (status & IntReq) /* Restore all interrupt sources. */ + outw(ioaddr + EL3_CMD, vp->status_enable); + if (status & HostError) { + u16 fifo_diag; + EL3WINDOW(4); + fifo_diag = inw(ioaddr + Wn4_FIFODiag); + if (vortex_debug > 0) + printk(KERN_ERR "%s: Host error, FIFO diagnostic register %4.4x.\n", + dev->name, fifo_diag); + /* Adapter failure requires Tx/Rx reset and reinit. */ + if (vp->full_bus_master_tx) { + outw(TotalReset | 0xff, ioaddr + EL3_CMD); + for (i = 2000; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + /* Re-enable the receiver. */ + outw(RxEnable, ioaddr + EL3_CMD); + outw(TxEnable, ioaddr + EL3_CMD); + } else if (fifo_diag & 0x0400) + do_tx_reset = 1; + if (fifo_diag & 0x3000) { + outw(RxReset, ioaddr + EL3_CMD); + for (i = 2000; i >= 0 ; i--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + /* Set the Rx filter to the current state. */ + set_rx_mode(dev); + outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ + outw(AckIntr | HostError, ioaddr + EL3_CMD); + } + } + if (do_tx_reset) { + int j; + outw(TxReset, ioaddr + EL3_CMD); + for (j = 200; j >= 0 ; j--) + if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) + break; + outw(TxEnable, ioaddr + EL3_CMD); + } + } + static int vortex_start_xmit(struct sk_buff *skb, struct device *dev) { struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; -#ifndef final_version - if (skb == NULL || skb->len <= 0) { - printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - dev_tint(dev); - return 0; - } -#endif - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start >= TX_TIMEOUT) vortex_tx_timeout(dev); @@ -1318,7 +1449,7 @@ #ifdef VORTEX_BUS_MASTER if (vp->bus_master) { /* Set the bus-master controller to transfer the packet. */ - outl((int)(skb->data), ioaddr + Wn7_MasterAddr); + outl(virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); vp->tx_skb = skb; outw(StartDMADown, ioaddr + EL3_CMD); @@ -1326,9 +1457,9 @@ } else { /* ... and the packet rounded to a doubleword. */ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb (skb, FREE_WRITE); + DEV_FREE_SKB(skb); if (inw(ioaddr + TxFree) > 1536) { - dev->tbusy = 0; + 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); @@ -1336,9 +1467,9 @@ #else /* ... and the packet rounded to a doubleword. */ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb (skb, FREE_WRITE); + DEV_FREE_SKB(skb); if (inw(ioaddr + TxFree) > 1536) { - dev->tbusy = 0; + 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); @@ -1348,20 +1479,20 @@ /* Clear the Tx status stack. */ { - short tx_status; - int i = 4; + int tx_status; + int i = 32; while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ if (vortex_debug > 2) - printk("%s: Tx error, status %2.2x.\n", + printk(KERN_DEBUG "%s: Tx error, status %2.2x.\n", dev->name, tx_status); if (tx_status & 0x04) vp->stats.tx_fifo_errors++; if (tx_status & 0x38) vp->stats.tx_aborted_errors++; if (tx_status & 0x30) { int j; outw(TxReset, ioaddr + EL3_CMD); - for (j = 20; j >= 0 ; j--) + for (j = 200; j >= 0 ; j--) if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } @@ -1379,15 +1510,6 @@ struct vortex_private *vp = (struct vortex_private *)dev->priv; int ioaddr = dev->base_addr; -#ifndef final_version - if (skb == NULL || skb->len <= 0) { - printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - dev_tint(dev); - return 0; - } -#endif - if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start >= TX_TIMEOUT) vortex_tx_timeout(dev); @@ -1401,11 +1523,11 @@ int i; if (vortex_debug > 3) - printk("%s: Trying to send a packet, Tx index %d.\n", + printk(KERN_DEBUG "%s: Trying to send a packet, Tx index %d.\n", dev->name, vp->cur_tx); if (vp->tx_full) { if (vortex_debug >0) - printk("%s: Tx Ring full, refusing to send buffer.\n", + printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n", dev->name); return 1; } @@ -1420,7 +1542,7 @@ cli(); outw(DownStall, ioaddr + EL3_CMD); /* Wait for the stall to complete. */ - for (i = 60; i >= 0 ; i--) + for (i = 600; i >= 0 ; i--) if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) break; prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); @@ -1436,7 +1558,7 @@ vp->tx_full = 1; else { /* Clear previous interrupt enable. */ prev_entry->status &= ~TxIntrUploaded; - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); } dev->trans_start = jiffies; return 0; @@ -1452,176 +1574,108 @@ #else struct device *dev = (struct device *)(irq2dev_map[irq]); #endif - struct vortex_private *lp; + struct vortex_private *vp; int ioaddr, status; int latency; - int i = max_interrupt_work; + int work_done = max_interrupt_work; - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - dev->interrupt = 1; + vp = (struct vortex_private *)dev->priv; + if (test_and_set_bit(0, (void*)&vp->in_interrupt)) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; ioaddr = dev->base_addr; latency = inb(ioaddr + Timer); - lp = (struct vortex_private *)dev->priv; status = inw(ioaddr + EL3_STATUS); if (vortex_debug > 4) - printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name, - status, latency); -#ifdef notdef - /* This code guard against bogus hangs, but fails with shared IRQs. */ - if ((status & ~0xE000) == 0x0000) { - static int donedidthis=0; - /* Some interrupt controllers store a bogus interrupt from boot-time. - Ignore a single early interrupt, but don't hang the machine for - other interrupt problems. */ - if (donedidthis++ > 100) { - printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", - dev->name, status, dev->start); - FREE_IRQ(dev->irq, dev); - } - } -#endif - + printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n", + dev->name, status, latency); do { if (vortex_debug > 5) - printk("%s: In interrupt loop, status %4.4x.\n", + printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n", dev->name, status); if (status & RxComplete) vortex_rx(dev); + if (status & UpComplete) { + outw(AckIntr | UpComplete, ioaddr + EL3_CMD); + boomerang_rx(dev); + } if (status & TxAvailable) { if (vortex_debug > 5) - printk(" TX room bit was handled.\n"); + printk(KERN_DEBUG " TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } - if (status & TxComplete) { /* Really "TxError" for us. */ - unsigned char tx_status = inb(ioaddr + TxStatus); - /* Presumably a tx-timeout. We must merely re-enable. */ - if (vortex_debug > 2 - || (tx_status != 0x88 && vortex_debug > 0)) - printk("%s: Transmit error, Tx status register %2.2x.\n", - dev->name, tx_status); - if (tx_status & 0x04) lp->stats.tx_fifo_errors++; - if (tx_status & 0x38) lp->stats.tx_aborted_errors++; - outb(0, ioaddr + TxStatus); - outw(TxEnable, ioaddr + EL3_CMD); - } + if (status & DownComplete) { - unsigned int dirty_tx = lp->dirty_tx; + unsigned int dirty_tx = vp->dirty_tx; - while (lp->cur_tx - dirty_tx > 0) { + while (vp->cur_tx - dirty_tx > 0) { int entry = dirty_tx % TX_RING_SIZE; if (inl(ioaddr + DownListPtr) == - virt_to_bus(&lp->tx_ring[entry])) + virt_to_bus(&vp->tx_ring[entry])) break; /* It still hasn't been processed. */ - if (lp->tx_skbuff[entry]) { - dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); - lp->tx_skbuff[entry] = 0; + if (vp->tx_skbuff[entry]) { + DEV_FREE_SKB(vp->tx_skbuff[entry]); + vp->tx_skbuff[entry] = 0; } - /* lp->stats.tx_packets++; Counted below. */ + /* vp->stats.tx_packets++; Counted below. */ dirty_tx++; } - lp->dirty_tx = dirty_tx; + vp->dirty_tx = dirty_tx; outw(AckIntr | DownComplete, ioaddr + EL3_CMD); - if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { - lp->tx_full= 0; - dev->tbusy = 0; + if (vp->tx_full && (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { + vp->tx_full= 0; + clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } } #ifdef VORTEX_BUS_MASTER if (status & DMADone) { outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ - dev->tbusy = 0; - dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */ + clear_bit(0, (void*)&dev->tbusy); + DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */ mark_bh(NET_BH); } #endif - if (status & UpComplete) { - boomerang_rx(dev); - outw(AckIntr | UpComplete, ioaddr + EL3_CMD); - } - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts at once. */ - if (status & RxEarly) { /* Rx early is unused. */ - vortex_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & StatsFull) { /* Empty statistics. */ - static int DoneDidThat = 0; - if (vortex_debug > 4) - printk("%s: Updating stats.\n", dev->name); - update_stats(ioaddr, dev); - /* DEBUG HACK: Disable statistics as an interrupt source. */ - /* This occurs when we have the wrong media type! */ - if (DoneDidThat == 0 && - inw(ioaddr + EL3_STATUS) & StatsFull) { - int win, reg; - printk("%s: Updating stats failed, disabling stats as an" - " interrupt source.\n", dev->name); - for (win = 0; win < 8; win++) { - EL3WINDOW(win); - printk("\n Vortex window %d:", win); - for (reg = 0; reg < 16; reg++) - printk(" %2.2x", inb(ioaddr+reg)); - } - EL3WINDOW(7); - outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure - | UpComplete | DownComplete | TxComplete, - ioaddr + EL3_CMD); - DoneDidThat++; - } - } - if (status & AdapterFailure) { - u16 fifo_diag; - EL3WINDOW(4); - fifo_diag = inw(ioaddr + Wn4_FIFODiag); - if (vortex_debug > 0) - printk("%s: Host error, FIFO diagnostic register %4.4x.\n", - dev->name, fifo_diag); - /* Adapter failure requires Tx/Rx reset and reinit. */ - if (fifo_diag & 0x0400) { - int j; - outw(TxReset, ioaddr + EL3_CMD); - for (j = 20; j >= 0 ; j--) - if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - outw(TxEnable, ioaddr + EL3_CMD); - } - if (fifo_diag & 0x2000) { - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } + /* Check for all uncommon interrupts at once. */ + if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) + vortex_error(dev, status); + + if (--work_done < 0) { + if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) { + /* Just ack these and return. */ + outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD); + } else { + printk(KERN_WARNING "%s: Too much work in interrupt, status " + "%4.4x. Temporarily disabling functions (%4.4x).\n", + dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); + /* Disable all pending interrupts. */ + outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); + outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); + /* Set a timer to reenable interrupts. */ + + break; } } - - if (--i < 0) { - printk("%s: Too much work in interrupt, status %4.4x. " - "Disabling functions (%4.4x).\n", - dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); - /* Disable all pending interrupts. */ - outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); - outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); - break; - } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); if (vortex_debug > 4) - printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status); + printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n", + dev->name, status); dev->interrupt = 0; + clear_bit(0, (void*)&vp->in_interrupt); return; } @@ -1634,13 +1688,13 @@ short rx_status; if (vortex_debug > 5) - printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", + printk(KERN_DEBUG" In rx_packet(), status %4.4x, rx_status %4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); while ((rx_status = inw(ioaddr + RxStatus)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ unsigned char rx_error = inb(ioaddr + RxErrors); if (vortex_debug > 2) - printk(" Rx error: status %2.2x.\n", rx_error); + printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; if (rx_error & 0x02) vp->stats.rx_length_errors++; @@ -1649,12 +1703,12 @@ if (rx_error & 0x10) vp->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; + int pkt_len = rx_status & 0x1fff; struct sk_buff *skb; skb = DEV_ALLOC_SKB(pkt_len + 5); if (vortex_debug > 4) - printk("Receiving packet size %d status %4.4x.\n", + printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; @@ -1680,8 +1734,8 @@ break; continue; } else if (vortex_debug) - printk("%s: Couldn't allocate a sk_buff of size %d.\n", - dev->name, pkt_len); + printk(KERN_NOTICE "%s: No memory to allocate a sk_buff of " + "size %d.\n", dev->name, pkt_len); } outw(RxDiscard, ioaddr + EL3_CMD); vp->stats.rx_dropped++; @@ -1701,15 +1755,17 @@ int entry = vp->cur_rx % RX_RING_SIZE; int ioaddr = dev->base_addr; int rx_status; + int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx; if (vortex_debug > 5) - printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", + printk(KERN_DEBUG " In boomerang_rx(), status %4.4x, rx_status " + "%4.4x.\n", inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { if (rx_status & RxDError) { /* Error, update stats. */ unsigned char rx_error = rx_status >> 16; if (vortex_debug > 2) - printk(" Rx error: status %2.2x.\n", rx_error); + printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; if (rx_error & 0x02) vp->stats.rx_length_errors++; @@ -1718,11 +1774,11 @@ if (rx_error & 0x10) vp->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; + int pkt_len = rx_status & 0x1fff; struct sk_buff *skb; if (vortex_debug > 4) - printk("Receiving packet size %d status %4.4x.\n", + printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); /* Check if the packet is long enough to just accept without @@ -1730,28 +1786,45 @@ if (pkt_len < rx_copybreak && (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), pkt_len); +#else + memcpy(skb->data, bus_to_virt(vp->rx_ring[entry].addr), pkt_len); + skb->len = pkt_len; +#endif rx_copy++; } else{ void *temp; /* Pass up the skbuff already on the Rx ring. */ skb = vp->rx_skbuff[entry]; 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) - printk("%s: Warning -- the skbuff addresses do not match" - " in boomerang_rx: %p vs. %p / %p.\n", dev->name, - bus_to_virt(vp->rx_ring[entry].addr), - skb->head, 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); rx_nocopy++; } #if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); + { /* Use hardware checksum info. */ + int csum_bits = rx_status & 0xee000000; + if (csum_bits && + (csum_bits == (IPChksumValid | TCPChksumValid) || + csum_bits == (IPChksumValid | UDPChksumValid))) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + rx_csumhits++; + } + } #else skb->len = pkt_len; #endif @@ -1760,13 +1833,15 @@ vp->stats.rx_packets++; } entry = (++vp->cur_rx) % RX_RING_SIZE; + if (--rx_work_limit < 0) + break; } /* Refill the Rx ring buffers. */ for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { struct sk_buff *skb; entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { - skb = dev_alloc_skb(PKT_BUF_SZ); + skb = DEV_ALLOC_SKB(PKT_BUF_SZ); if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */ @@ -1779,6 +1854,7 @@ vp->rx_skbuff[entry] = skb; } vp->rx_ring[entry].status = 0; /* Clear complete bit. */ + outw(UpUnstall, ioaddr + EL3_CMD); } return 0; } @@ -1794,16 +1870,16 @@ dev->tbusy = 1; if (vortex_debug > 1) { - printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", + printk(KERN_DEBUG"%s: vortex_close() status %4.4x, Tx status %2.2x.\n", dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); - printk("%s: vortex close stats: rx_nocopy %d rx_copy %d" - " tx_queued %d.\n", - dev->name, rx_nocopy, rx_copy, queued_packet); + printk(KERN_DEBUG "%s: vortex close stats: rx_nocopy %d rx_copy %d" + " tx_queued %d Rx pre-checksummed %d.\n", + dev->name, rx_nocopy, rx_copy, queued_packet, rx_csumhits); } del_timer(&vp->timer); - /* Turn off statistics ASAP. We update lp->stats below. */ + /* Turn off statistics ASAP. We update vp->stats below. */ outw(StatsDisable, ioaddr + EL3_CMD); /* Disable the receiver and transmitter. */ @@ -1831,7 +1907,7 @@ #if LINUX_VERSION_CODE < 0x20100 vp->rx_skbuff[i]->free = 1; #endif - dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE); + DEV_FREE_SKB(vp->rx_skbuff[i]); vp->rx_skbuff[i] = 0; } } @@ -1839,7 +1915,7 @@ outl(0, ioaddr + DownListPtr); for (i = 0; i < TX_RING_SIZE; i++) if (vp->tx_skbuff[i]) { - dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE); + DEV_FREE_SKB(vp->tx_skbuff[i]); vp->tx_skbuff[i] = 0; } } @@ -1911,7 +1987,7 @@ int phy = vp->phys[0] & 0x1f; if (vortex_debug > 2) - printk("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", + 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]); @@ -1941,11 +2017,11 @@ set_rx_mode(struct device *dev) { int ioaddr = dev->base_addr; - short new_mode; + int new_mode; if (dev->flags & IFF_PROMISC) { - if (vortex_debug > 3) - printk("%s: Setting promiscuous mode.\n", dev->name); + if (vortex_debug > 0) + printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name); new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; @@ -1962,6 +2038,97 @@ set_rx_mode(dev); } #endif + + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* 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_SHIFT_CLK 0x01 +#define MDIO_DIR_WRITE 0x04 +#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) +#define MDIO_DATA_READ 0x02 +#define MDIO_ENB_IN 0x00 + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(int ioaddr, int bits) +{ + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + + /* Establish sync by sending at least 32 logic ones. */ + while (-- bits >= 0) { + outw(MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } +} + +static int mdio_read(int 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; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the read command bits out. */ + for (i = 14; i >= 0; i--) { + int dataval = (read_cmd&(1< 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return retval>>1 & 0xffff; +} + +static void mdio_write(int ioaddr, int phy_id, int location, int value) +{ + int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; + int mdio_addr = ioaddr + Wn4_PhysicalMgmt; + int i; + + if (mii_preamble_required) + mdio_sync(ioaddr, 32); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (write_cmd&(1<= 0; i--) { + outw(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + + return; +} + #ifdef MODULE void @@ -1969,6 +2136,10 @@ { struct device *next_dev; +#ifdef CARDBUS + unregister_driver(&vortex_ops); +#endif + /* 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; @@ -1984,7 +2155,9 @@ /* * Local variables: - * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c" + * 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" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --recursive --new-file v2.0.33/linux/drivers/net/Changelog.tlan linux/drivers/net/Changelog.tlan --- v2.0.33/linux/drivers/net/Changelog.tlan Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/Changelog.tlan Wed Jun 3 15:17:47 1998 @@ -0,0 +1,96 @@ +TLan Device Driver change log. + +0.38 - Added code to isolate the external PHY if the internal PHY is + being used for AUI/BNC connectivity. Also set the aui and + debug variables from mem_start and mem_end if the driver is + built into the kernel. + +0.37 - Added TLAN_PHY_ACTIVITY flag for Unmanaged PHY. + - If aui is selected and the card is not a 0xF130 (unmanaged phy), + select the builtin PHY and put it in AUI mode. I don't know if + this will work, but it is my best guess as to how AUI/BNC + functionality is being provided for the external managed PHYs, + which don't support AUI/BNC. + - If aui is set and the card is a 0xF130, set the MTXD1 bit in + the ACOMMIT register, to select AUI mode on the unmanaged PHY. + I don't know if this is necessary. + +0.36 - Changed AN_ADV register to not advertise full duplex modes. + 100Mbs should work now on 0xAE32. + - Fixed a small bug where heartbeat and PHY interrupts were + always being enabled. + - Force the driver into Unmanaged PHY mode for 0xF130 devices, + even if a managed (ie, the built-in one) PHY is detected. + - Moved the PHY initialization to after the onboard PHY is enabled, + if selected. + +0.35 - Added entry for Level One LXT970 PHY. + - Commented out instruction to set phyOnline to 1 in DP Check. + +0.34 - Revised Reset routine. + - Added support for 0xF130 device. + - Added entry for NS DP83840A Rev 0 PHY. + +0.33 - Major Reformatting of comments to fit in 80 columns. + - Changed tabs to 8 spaces, reformatted accordingly. + - Added code to check and change polarity. + - Added caveats to README. + - Small fix to Makefile to make unversioned modules correctly. + - Redid PhySelect to list all phy ids then choose one. + +0.32 - Put in another change for BNC stuff that I mistakenly + omitted in 0.31. + +0.31 - Completed BNC changes (at least one person had it working). + - Added another device id. + - Changed debugging messages to hopefully be more useful. + +0.30 - Added PCI device IDs provided by Don Dupuis of Compaq. + - Turned of MINTEN when using 10/100 PHY. + - Added ability to select AUI at insmod time. What will this do + for BNC, if anything? + - Tweaked with names. + +0.29 Fixed problem with older TLAN chips (pre-PG3.0 didn't have + EOC bit in lists nor INTDIS register). Added support for NetFlex + card. + +0.28 Changed a lot of commented out printfs to debugging statements. + Added detail to product names. Added 2nd reset to open command. + Don't know why, just have to, at least for Netelligent 10 card. + +0.27 Created a tlan_probe function for compiled-in-kernel use. + Also added debugging macro, which could be reviewed. Is there + a kernel variable which stores desired verbosity/debug level? + +0.26 Change Invalid interrupt handler to return 0, not print out debug + message. This was causing lots of messages to be printed out with + shared interrupts. + +0.25 + Added IDs for integrated NetFlex-3 controller. Not sure if it is even + TLAN yet, but we'll see. + +0.24 + Increased second delay in internal PHY check routine, as driver wasn't + starting on the first insmod. + +0.23 + Added documentation to function preambles. + Removed some commented out printk's. + Tested Rx and Tx busy frequency with current queue sizes (no busies). + +0.22 + Cleared tbusy flag at adapter check. + Removed some commented out printk's. + Added TLan_SetMulticastList routine. + +0.21 + Got all virt_to_bus calls in place for 2.1 kernels. + +0.20 + Rewrote buffer/list subsystem to use bounce buffers as DMA + doesn't work over 16 Meg. Why? This is PCI not ISA. + Fixed a problem with buffers stalling due to a race condition + in the kernel. + diff -u --recursive --new-file v2.0.33/linux/drivers/net/Config.in linux/drivers/net/Config.in --- v2.0.33/linux/drivers/net/Config.in Tue Aug 12 20:29:26 1997 +++ linux/drivers/net/Config.in Wed Jun 3 15:17:47 1998 @@ -41,7 +41,7 @@ # bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET if [ "$CONFIG_NET_ETHERNET" = "y" ]; then - bool '3COM cards' CONFIG_NET_VENDOR_3COM + bool '3COM ISA, EISA and PCI cards' CONFIG_NET_VENDOR_3COM if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then tristate '3c501 support' CONFIG_EL1 tristate '3c503 support' CONFIG_EL2 @@ -50,24 +50,35 @@ tristate '3c507 support' CONFIG_EL16 fi tristate '3c509/3c579 support' CONFIG_EL3 + tristate '3c515 ISA Fast EtherLink' CONFIG_3C515 tristate '3c590/3c900 series (592/595/597/900/905) "Vortex/Boomerang" support' CONFIG_VORTEX fi - bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE - if [ "$CONFIG_LANCE" = "y" ]; then - bool 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32 - fi - bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC + bool 'Western Digital/SMC ISA and EISA cards' CONFIG_NET_VENDOR_SMC if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then tristate 'WD80*3 support' CONFIG_WD80x3 tristate 'SMC Ultra support' CONFIG_ULTRA tristate 'SMC Ultra32 support' CONFIG_ULTRA32 tristate 'SMC 9194 support' CONFIG_SMC9194 fi + bool 'PCI Ethernet adapters' CONFIG_NET_PCI + if [ "$CONFIG_NET_PCI" = "y" ]; then + tristate 'AMD PCI PCnet32 (PCI bus NE2100 cards) support' CONFIG_PCNET32 + tristate 'Intel EtherExpressPro PCI 10+/100B/100+ support' CONFIG_EEXPRESS_PRO100B + tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5 + tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP + tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS + tristate 'PCI NE2000 support' CONFIG_NE2K_PCI + tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN + tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 + tristate 'SMC EPIC/100 (EtherPower II) support' CONFIG_EPIC + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN + fi + fi bool 'Other ISA cards' CONFIG_NET_ISA if [ "$CONFIG_NET_ISA" = "y" ]; then - if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'AT1700 support (EXPERIMENTAL)' CONFIG_AT1700 - fi + tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE + tristate 'AT1700 (Fujitsu 86965) support' CONFIG_AT1700 tristate 'Cabletron E21xx support' CONFIG_E2100 tristate 'DEPCA, DE10x, DE200, DE201, DE202, DE422 support' CONFIG_DEPCA tristate 'EtherWORKS 3 (DE203, DE204, DE205) support' CONFIG_EWRK3 @@ -82,7 +93,7 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'ICL EtherTeam 16i/32 support' CONFIG_ETH16I fi - tristate 'NE2000/NE1000 support' CONFIG_NE2000 + tristate 'NE2000/NE1000 ISA support' CONFIG_NE2000 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'NI5210 support' CONFIG_NI52 tristate 'NI6510 support' CONFIG_NI65 @@ -92,23 +103,19 @@ fi bool 'SK_G16 support' CONFIG_SK_G16 fi - bool 'EISA, VLB, PCI and on board controllers' CONFIG_NET_EISA + bool 'EISA, VLB and other board controllers' CONFIG_NET_EISA if [ "$CONFIG_NET_EISA" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 fi tristate 'Apricot Xen-II on board ethernet' CONFIG_APRICOT - tristate 'Intel EtherExpress/Pro 100B support' CONFIG_EEXPRESS_PRO100B - tristate 'DE425, DE434, DE435, DE450, DE500 support' CONFIG_DE4X5 - tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP - tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET fi fi bool 'Pocket and portable adaptors' CONFIG_NET_POCKET if [ "$CONFIG_NET_POCKET" = "y" ]; then - bool 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP + tristate 'AT-LAN-TEC/RealTek pocket adaptor support' CONFIG_ATP tristate 'D-Link DE600 pocket adaptor support' CONFIG_DE600 tristate 'D-Link DE620 pocket adaptor support' CONFIG_DE620 fi diff -u --recursive --new-file v2.0.33/linux/drivers/net/Makefile linux/drivers/net/Makefile --- v2.0.33/linux/drivers/net/Makefile Tue Aug 12 20:29:26 1997 +++ linux/drivers/net/Makefile Wed Jun 3 15:17:47 1998 @@ -148,7 +148,7 @@ L_OBJS += smc-ultra32.o CONFIG_8390_BUILTIN = y else - ifeq ($(CONFIG_ULTRA),m) + ifeq ($(CONFIG_ULTRA32),m) CONFIG_8390_MODULE = y M_OBJS += smc-ultra32.o endif @@ -247,8 +247,9 @@ ifeq ($(CONFIG_LANCE),y) L_OBJS += lance.o - ifeq ($(CONFIG_LANCE32),y) - L_OBJS += lance32.o +else + ifeq ($(CONFIG_LANCE),m) + M_OBJS += lance.o endif endif @@ -296,6 +297,14 @@ endif endif +ifeq ($(CONFIG_3C515),y) +L_OBJS += 3c515.o +else + ifeq ($(CONFIG_3C515),m) + M_OBJS += 3c515.o + endif +endif + ifeq ($(CONFIG_VORTEX),y) L_OBJS += 3c59x.o else @@ -328,6 +337,47 @@ endif endif +ifeq ($(CONFIG_EPIC),y) +L_OBJS += epic100.o +else + ifeq ($(CONFIG_EPIC),m) + M_OBJS += epic100.o + endif +endif + +ifeq ($(CONFIG_NE2K_PCI),y) +L_OBJS += ne2k-pci.o +CONFIG_8390_BUILTIN = y +else + ifeq ($(CONFIG_NE2K_PCI),m) + CONFIG_8390_MODULE = y + M_OBJS += ne2k-pci.o + endif +endif + +ifeq ($(CONFIG_PCNET32),y) +L_OBJS += pcnet32.o +else + ifeq ($(CONFIG_PCNET32),m) + M_OBJS += pcnet32.o + endif +endif + +ifeq ($(CONFIG_RTL8139),y) +L_OBJS += rtl8139.o +else + ifeq ($(CONFIG_RTL8139),m) + M_OBJS += rtl8139.o + endif +endif + +ifeq ($(CONFIG_YELLOWFIN),y) +L_OBJS += yellowfin.o +else + ifeq ($(CONFIG_YELLOWFIN),m) + M_OBJS += yellowfin.o + endif +endif ifeq ($(CONFIG_WAVELAN),y) L_OBJS += wavelan.o @@ -359,6 +409,10 @@ ifeq ($(CONFIG_ATP),y) L_OBJS += atp.o +else + ifeq ($(CONFIG_ATP),m) + M_OBJS += atp.o + endif endif ifeq ($(CONFIG_DE4X5),y) @@ -420,6 +474,14 @@ else ifeq ($(CONFIG_DEC_ELCP),m) M_OBJS += tulip.o + endif +endif + +ifeq ($(CONFIG_TLAN),y) +L_OBJS += tlan.o +else + ifeq ($(CONFIG_TLAN),m) + M_OBJS += tlan.o endif endif diff -u --recursive --new-file v2.0.33/linux/drivers/net/Space.c linux/drivers/net/Space.c --- v2.0.33/linux/drivers/net/Space.c Tue Aug 12 20:29:26 1997 +++ linux/drivers/net/Space.c Wed Jun 3 15:17:47 1998 @@ -45,12 +45,12 @@ extern int wd_probe(struct device *dev); extern int el2_probe(struct device *dev); extern int ne_probe(struct device *dev); +extern int ne2k_pci_probe(struct device *dev); extern int hp_probe(struct device *dev); extern int hp_plus_probe(struct device *dev); extern int znet_probe(struct device *); extern int express_probe(struct device *); extern int eepro_probe(struct device *); -extern int eepro100_probe(struct device *); extern int el3_probe(struct device *); extern int at1500_probe(struct device *); extern int at1700_probe(struct device *); @@ -80,7 +80,14 @@ extern int a2065_probe(struct device *); extern int ariadne_probe(struct device *); extern int hydra_probe(struct device *); - +extern int yellowfin_probe(struct device *); +extern int eepro100_probe(struct device *); +extern int epic100_probe(struct device *); +extern int rtl8139_probe(struct device *); +extern int tlan_probe(struct device *); +extern int isa515_probe(struct device *); +extern int pcnet32_probe(struct device *); +extern int lance_probe(struct device *); /* Detachable devices ("pocket adaptors") */ extern int atp_init(struct device *); extern int de600_probe(struct device *); @@ -95,27 +102,54 @@ return 1; /* ENXIO */ if (1 + /* All PCI probes are safe, and thus should be first. */ +#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */ + && de4x5_probe(dev) +#endif #ifdef CONFIG_DGRS && dgrs_probe(dev) #endif +#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */ + && eepro100_probe(dev) +#endif +#ifdef CONFIG_EPIC + && epic100_probe(dev) +#endif +#if defined(CONFIG_HP100) + && hp100_probe(dev) +#endif +#if defined(CONFIG_NE2K_PCI) + && ne2k_pci_probe(dev) +#endif +#ifdef CONFIG_PCNET32 + && pcnet32_probe(dev) +#endif +#ifdef CONFIG_RTL8139 + && rtl8139_probe(dev) +#endif #if defined(CONFIG_VORTEX) && tc59x_probe(dev) #endif -#if defined(CONFIG_SEEQ8005) - && seeq8005_probe(dev) -#endif #if defined(CONFIG_DEC_ELCP) && tulip_probe(dev) #endif -#if defined(CONFIG_HP100) - && hp100_probe(dev) -#endif -#if defined(CONFIG_ULTRA) - && ultra_probe(dev) +#ifdef CONFIG_YELLOWFIN + && yellowfin_probe(dev) +#endif + /* Next mostly-safe EISA-only drivers. */ +#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */ + && ac3200_probe(dev) #endif #if defined(CONFIG_ULTRA32) && ultra32_probe(dev) #endif + /* Third, sensitive ISA boards. */ +#ifdef CONFIG_AT1700 + && at1700_probe(dev) +#endif +#if defined(CONFIG_ULTRA) + && ultra_probe(dev) +#endif #if defined(CONFIG_SMC9194) && smc_init(dev) #endif @@ -131,21 +165,18 @@ #if defined(CONFIG_HPLAN_PLUS) && hp_plus_probe(dev) #endif -#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */ - && ac3200_probe(dev) +#if defined(CONFIG_SEEQ8005) + && seeq8005_probe(dev) #endif #ifdef CONFIG_E2100 /* Cabletron E21xx series. */ && e2100_probe(dev) #endif -#if defined(CONFIG_NE2000) || defined(NE2000) +#if defined(CONFIG_NE2000) && ne_probe(dev) #endif #ifdef CONFIG_AT1500 && at1500_probe(dev) #endif -#ifdef CONFIG_AT1700 - && at1700_probe(dev) -#endif #ifdef CONFIG_FMV18X /* Fujitsu FMV-181/182 */ && fmv18x_probe(dev) #endif @@ -155,6 +186,9 @@ #ifdef CONFIG_EL3 /* 3c509 */ && el3_probe(dev) #endif +#ifdef CONFIG_3C515 /* 3c515 */ + && tc515_probe(dev) +#endif #ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */ && znet_probe(dev) #endif @@ -164,18 +198,12 @@ #ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */ && eepro_probe(dev) #endif -#ifdef CONFIG_EEXPRESS_PRO100B /* Intel EtherExpress Pro100B */ - && eepro100_probe(dev) -#endif #ifdef CONFIG_DEPCA /* DEC DEPCA */ && depca_probe(dev) #endif #ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */ && ewrk3_probe(dev) #endif -#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */ - && de4x5_probe(dev) -#endif #ifdef CONFIG_APRICOT /* Apricot I82596 */ && apricot_probe(dev) #endif @@ -206,6 +234,9 @@ #ifdef CONFIG_NI65 && ni65_probe(dev) #endif +#ifdef CONFIG_LANCE /* ISA LANCE boards */ + && lance_probe(dev) +#endif #ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */ && atarilance_probe(dev) #endif @@ -220,6 +251,15 @@ #endif #ifdef CONFIG_SUNLANCE && sparc_lance_probe(dev) +#endif +#ifdef CONFIG_TLAN + && tlan_probe(dev) +#endif +#ifdef CONFIG_PCNET32 + && pcnet32_probe(dev) +#endif +#ifdef CONFIG_LANCE + && lance_probe(dev) #endif && 1 ) { return 1; /* -ENODEV or -EAGAIN would be more accurate. */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c --- v2.0.33/linux/drivers/net/arcnet.c Sat Oct 19 00:36:20 1996 +++ linux/drivers/net/arcnet.c Wed Jun 3 15:17:47 1998 @@ -373,7 +373,7 @@ /* macros to simplify debug checking */ #define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x)) -#define BUGMSG2(x,msg,args...) BUGLVL(x) printk(msg, ## args) +#define BUGMSG2(x,msg,args...) do{BUGLVL(x) printk(msg, ## args);}while(0) #define BUGMSG(x,msg,args...) BUGMSG2(x,"%s%6s: " msg, \ x==D_NORMAL ? KERN_WARNING : \ x<=D_INIT_REASONS ? KERN_INFO : KERN_DEBUG , \ diff -u --recursive --new-file v2.0.33/linux/drivers/net/at1700.c linux/drivers/net/at1700.c --- v2.0.33/linux/drivers/net/at1700.c Thu Feb 29 21:50:43 1996 +++ linux/drivers/net/at1700.c Wed Jun 3 15:17:47 1998 @@ -1,6 +1,6 @@ /* at1700.c: A network device driver for the Allied Telesis AT1700. - Written 1993-94 by Donald Becker. + Written 1993-98 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. @@ -29,7 +29,7 @@ */ static const char *version = - "at1700.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + "at1700.c:v1.15 4/7/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; #include @@ -53,9 +53,17 @@ #include #include -/* This unusual address order is used to verify the CONFIG register. */ +/* Tunable parameters. */ + +/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */ +#define MC_FILTERBREAK 64 + +/* These unusual address orders are used to verify the CONFIG register. */ static int at1700_probe_list[] = {0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0}; +static int fmv18x_probe_list[] = +{0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0}; + /* use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG @@ -68,7 +76,10 @@ /* Information that need to be kept for each board. */ struct net_local { struct enet_statistics stats; - uint tx_started:1; /* Number of packet on the Tx queue. */ + unsigned char mc_filter[8]; + uint jumpered:1; /* Set iff the board has jumper config. */ + uint tx_started:1; /* Packets are on the Tx queue. */ + uint invalid_irq:1; uchar tx_queue; /* Number of packet on the Tx queue. */ ushort tx_queue_len; /* Current length of the Tx queue. */ }; @@ -88,32 +99,19 @@ #define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */ #define TX_START 10 #define MODE13 13 +/* Configuration registers only on the '865A/B chips. */ #define EEPROM_Ctrl 16 #define EEPROM_Data 17 -#define IOCONFIG 19 +#define IOCONFIG 18 /* Either read the jumper, or move the I/O. */ +#define IOCONFIG1 19 +#define SAPROM 20 /* The station address PROM, if no EEPROM. */ #define RESET 31 /* Write to reset some parts of the chip. */ #define AT1700_IO_EXTENT 32 - -/* EEPROM_Ctrl bits. */ -#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */ -#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */ -#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */ -#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */ - -/* Delay between EEPROM clock transitions. */ -#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) - -/* 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) - - /* Index to functions, as function prototypes. */ extern int at1700_probe(struct device *dev); -static int at1700_probe1(struct device *dev, short ioaddr); +static int at1700_probe1(struct device *dev, int ioaddr); static int read_eeprom(int ioaddr, int location); static int net_open(struct device *dev); static int net_send_packet(struct sk_buff *skb, struct device *dev); @@ -121,7 +119,7 @@ static void net_rx(struct device *dev); static int net_close(struct device *dev); static struct enet_statistics *net_get_stats(struct device *dev); -static void set_multicast_list(struct device *dev); +static void set_rx_mode(struct device *dev); /* Check for a network adaptor of this type, and return '0' iff one exists. @@ -167,10 +165,11 @@ that can be done is checking a few bits and then diving right into an EEPROM read. */ -int at1700_probe1(struct device *dev, short ioaddr) +int at1700_probe1(struct device *dev, int ioaddr) { - char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15}; - unsigned int i, irq; + char fmv_irqmap[4] = {3, 7, 10, 15}; + char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15}; + unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0; /* Resetting the chip doesn't reset the ISA interface, so don't bother. That means we have to be careful with the register values we probe for. @@ -180,38 +179,42 @@ ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5), read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl)); #endif - if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr - || read_eeprom(ioaddr, 4) != 0x0000 - || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400) + /* We must check for the EEPROM-config boards first, else accessing + IOCONFIG0 will move the board! */ + if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr + && read_eeprom(ioaddr, 4) == 0x0000 + && (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400) + is_at1700 = 1; + else if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] == ioaddr + && inb(ioaddr + SAPROM ) == 0x00 + && inb(ioaddr + SAPROM + 1) == 0x00 + && inb(ioaddr + SAPROM + 2) == 0x0e) + is_fmv18x = 1; + else return -ENODEV; /* Reset the internal state machines. */ outb(0, ioaddr + RESET); - irq = irqmap[(read_eeprom(ioaddr, 12)&0x04) - | (read_eeprom(ioaddr, 0)>>14)]; - - /* Snarf the interrupt vector now. */ - if (request_irq(irq, &net_interrupt, 0, "at1700", NULL)) { - printk ("AT1700 found at %#3x, but it's unusable due to a conflict on" - "IRQ %d.\n", ioaddr, irq); - return EAGAIN; - } - /* Allocate a new 'dev' if needed. */ if (dev == NULL) dev = init_etherdev(0, sizeof(struct net_local)); + if (is_at1700) + irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04) + | (read_eeprom(ioaddr, 0)>>14)]; + else + irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03]; + /* Grab the region so that we can find another board if the IRQ request fails. */ - request_region(ioaddr, AT1700_IO_EXTENT, "at1700"); + request_region(ioaddr, AT1700_IO_EXTENT, dev->name); printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name, ioaddr, irq); dev->base_addr = ioaddr; dev->irq = irq; - irq2dev_map[irq] = dev; for(i = 0; i < 3; i++) { unsigned short eeprom_val = read_eeprom(ioaddr, 4+i); @@ -234,12 +237,12 @@ } /* Set the station address in bank zero. */ - outb(0xe0, ioaddr + 7); + outb(0xe0, ioaddr + CONFIG_1); for (i = 0; i < 6; i++) outb(dev->dev_addr[i], ioaddr + 8 + i); /* Switch to bank 1 and set the multicast table to accept none. */ - outb(0xe4, ioaddr + 7); + outb(0xe4, ioaddr + CONFIG_1); for (i = 0; i < 8; i++) outb(0x00, ioaddr + 8 + i); @@ -248,7 +251,7 @@ outb(0xda, ioaddr + CONFIG_0); /* Switch to bank 2 and lock our I/O address. */ - outb(0xe8, ioaddr + 7); + outb(0xe8, ioaddr + CONFIG_1); outb(dev->if_port, MODE13); /* Power-down the chip. Aren't we green! */ @@ -267,50 +270,72 @@ dev->stop = net_close; dev->hard_start_xmit = net_send_packet; dev->get_stats = net_get_stats; - dev->set_multicast_list = &set_multicast_list; + dev->set_multicast_list = &set_rx_mode; /* Fill in the fields of 'dev' with ethernet-generic values. */ - ether_setup(dev); + + { + struct net_local *lp = (struct net_local *)dev->priv; + lp->jumpered = is_fmv18x; + /* Snarf the interrupt vector now. */ + if (request_irq(irq, &net_interrupt, 0, dev->name, dev)) { + printk (" AT1700 at %#3x is unusable due to a conflict on" + "IRQ %d.\n", ioaddr, irq); + lp->invalid_irq = 1; + return 0; + } + } + return 0; } + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */ +#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */ +#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */ +#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */ + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay() do {} while (0); + +/* 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) { int i; unsigned short retval = 0; - short ee_addr = ioaddr + EEPROM_Ctrl; - short ee_daddr = ioaddr + EEPROM_Data; + int ee_addr = ioaddr + EEPROM_Ctrl; + int ee_daddr = ioaddr + EEPROM_Data; int read_cmd = location | EE_READ_CMD; - short ctrl_val = EE_CS; - - outb(ctrl_val, ee_addr); - + /* Shift the read command bits out. */ for (i = 9; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outb(EE_CS, ee_addr); outb(dataval, ee_daddr); - outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */ eeprom_delay(); - outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */ + outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */ eeprom_delay(); } - outb(EE_CS, ee_addr); - + outb(EE_DATA_WRITE, ee_daddr); for (i = 16; i > 0; i--) { + outb(EE_CS, ee_addr); + eeprom_delay(); outb(EE_CS | EE_SHIFT_CLK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0); - outb(EE_CS, ee_addr); - eeprom_delay(); } /* Terminate the EEPROM access. */ - ctrl_val &= ~EE_CS; - outb(ctrl_val | EE_SHIFT_CLK, ee_addr); - eeprom_delay(); - outb(ctrl_val, ee_addr); + outb(EE_CS, ee_addr); eeprom_delay(); + outb(EE_SHIFT_CLK, ee_addr); + outb(0, ee_addr); return retval; } @@ -330,7 +355,7 @@ outb(dev->dev_addr[i], ioaddr + 8 + i); /* Switch to bank 1 and set the multicast table to accept none. */ - outb(0xe4, ioaddr + 7); + outb(0xe4, ioaddr + CONFIG_1); for (i = 0; i < 8; i++) outb(0x00, ioaddr + 8 + i); @@ -338,10 +363,8 @@ bus access, and two 4K Tx queues. */ outb(0xda, ioaddr + CONFIG_0); - /* Same config 0, except enable the Rx and Tx. */ - outb(0x5a, ioaddr + CONFIG_0); - /* Switch to register bank 2 for the run-time registers. */ - outb(0xe8, ioaddr + CONFIG_1); + /* Switch to register bank 2, enable the Rx and Tx. */ + outw(0xe85a, ioaddr + CONFIG_0); lp->tx_started = 0; lp->tx_queue = 0; @@ -410,7 +433,7 @@ /* Turn off the possible Tx interrupts. */ outb(0x00, ioaddr + TX_INTR); - + outw(length, ioaddr + DATAPORT); outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); @@ -442,7 +465,7 @@ static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - struct device *dev = (struct device *)(irq2dev_map[irq]); + struct device *dev = dev_id; struct net_local *lp; int ioaddr, status; @@ -563,7 +586,7 @@ } if (net_debug > 5) - printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", + printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); } return; @@ -572,6 +595,7 @@ /* The inverse routine to net_open(). */ static int net_close(struct device *dev) { + struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; dev->tbusy = 1; @@ -580,7 +604,15 @@ /* Set configuration register 0 to disable Tx and Rx. */ outb(0xda, ioaddr + CONFIG_0); - /* Update the statistics -- ToDo. */ + /* No statistic counters on the chip to update. */ + +#if 0 + /* Disable the IRQ on boards where it is feasible. */ + if (lp->jumpered) { + outb(0x00, ioaddr + IOCONFIG1); + free_irq(dev->irq, dev); + } +#endif /* Power-down the chip. Green, green, green! */ outb(0x00, ioaddr + CONFIG_1); @@ -590,44 +622,90 @@ return 0; } -/* Get the current statistics. This may be called with the card open or - closed. */ +/* Get the current statistics. + This may be called with the card open or closed. + There are no on-chip counters, so this function is trivial. +*/ static struct enet_statistics * net_get_stats(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; + return &lp->stats; +} - cli(); - /* ToDo: Update the statistics from the device registers. */ - sti(); +/* + Set the multicast/promiscuous mode for this adaptor. +*/ - return &lp->stats; +/* 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) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + 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; } -/* Set or clear the multicast filter for this adaptor. - num_addrs == -1 Promiscuous mode, receive all packets - num_addrs == 0 Normal mode, clear multicast list - num_addrs > 0 Multicast mode, receive normal and MC packets, and do - best-effort filtering. - */ static void -set_multicast_list(struct device *dev) +set_rx_mode(struct device *dev) { - short ioaddr = dev->base_addr; - if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) - { - /* - * We must make the kernel realise we had to move - * into promisc mode or we start all out war on - * the cable. - AC - */ - dev->flags|=IFF_PROMISC; - + int ioaddr = dev->base_addr; + struct net_local *lp = (struct net_local *)dev->priv; + unsigned char mc_filter[8]; /* Multicast hash filter */ + long flags; + int i; + + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + memset(mc_filter, 0xff, sizeof(mc_filter)); outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ - } - else - outb(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */ + } else if (dev->mc_count > MC_FILTERBREAK + || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + outb(2, ioaddr + RX_MODE); /* Use normal mode. */ + } else if (dev->mc_count == 0) { + memset(mc_filter, 0x00, sizeof(mc_filter)); + outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ + } else { + struct dev_mc_list *mclist; + int i; + + memset(mc_filter, 0, sizeof(mc_filter)); + 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) >> 26, + mc_filter); + } + + save_flags(flags); + cli(); + if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) { + int saved_bank = inw(ioaddr + CONFIG_0); + /* Switch to bank 1 and set the multicast table. */ + outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0); + for (i = 0; i < 8; i++) + outb(mc_filter[i], ioaddr + 8 + i); + memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter)); + outw(saved_bank, ioaddr + CONFIG_0); + } + restore_flags(flags); + return; } + #ifdef MODULE static char devicename[9] = { 0, }; static struct device dev_at1700 = { @@ -661,17 +739,16 @@ /* If we don't do this, we can't re-insmod it later. */ free_irq(dev_at1700.irq, NULL); - irq2dev_map[dev_at1700.irq] = NULL; release_region(dev_at1700.base_addr, AT1700_IO_EXTENT); } #endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c at1700.c" - * version-control: t - * kept-new-versions: 5 + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" + * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" * tab-width: 4 + * c-basic-offset: 4 * c-indent-level: 4 * End: */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c --- v2.0.33/linux/drivers/net/auto_irq.c Mon Aug 4 12:10:18 1997 +++ linux/drivers/net/auto_irq.c Wed Jun 3 15:17:47 1998 @@ -39,7 +39,7 @@ #include #include -struct device *irq2dev_map[16] = {0, 0, /* ... zeroed */}; +struct device *irq2dev_map[NR_IRQS] = {0, 0, /* ... zeroed */}; unsigned long irqs_busy = 0x2147; /* The set of fixed IRQs (keyboard, timer, etc) */ unsigned long irqs_used = 0x0001; /* The set of fixed IRQs sometimes enabled. */ @@ -65,8 +65,8 @@ int autoirq_setup(int waittime) { int i; - int timeout = jiffies + waittime; - int boguscount = (waittime*loops_per_sec) / 100; + unsigned long timeout = jiffies + waittime; + unsigned long boguscount = (waittime*loops_per_sec) / 100; irq_handled = 0; irq_bitmap = 0; @@ -93,8 +93,8 @@ int autoirq_report(int waittime) { int i; - int timeout = jiffies+waittime; - int boguscount = (waittime*loops_per_sec) / 100; + unsigned long timeout = jiffies+waittime; + unsigned long boguscount = (waittime*loops_per_sec) / 100; /* Hang out at least jiffies waiting for the IRQ. */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c --- v2.0.33/linux/drivers/net/de4x5.c Tue Aug 12 13:21:12 1997 +++ linux/drivers/net/de4x5.c Wed Jun 3 15:17:47 1998 @@ -41,12 +41,13 @@ Digital Semiconductor SROM Specification. The driver currently recognises the following chips: - DC21040 (no SROM) - DC21041[A] - DC21140[A] + DC21040 (no SROM) + DC21041[A] + DC21140[A] + DC21142 + DC21143 - I plan to add DC2114[23] support ASAP, time permitting. So far the - driver is known to work with the following cards: + So far the driver is known to work with the following cards: KINGSTON Linksys @@ -101,7 +102,7 @@ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. 2) for fixed autoprobes (not recommended), edit the source code near - line 5005 to reflect the I/O address you're using, or assign these when + line 5594 to reflect the I/O address you're using, or assign these when loading by: insmod de4x5 io=0xghh where g = bus number @@ -179,9 +180,53 @@ INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not run on the same interrupt. PCMCIA/CardBus is another can of worms... + Finally, I think I have really fixed the module loading problem with + more than one DECchip based card. As a side effect, I don't mess with + the device structure any more which means that if more than 1 card in + 2.0.x is installed (4 in 2.1.x), the user will have to edit + linux/drivers/net/Space.c to make room for them. Hence, module loading + is the preferred way to use this driver, since it doesn't have this + limitation. + + Where SROM media detection is used and full duplex is specified in the + SROM, the feature is ignored unless lp->params.fdx is set at compile + time OR during a module load (insmod de4x5 args='eth??:fdx' [see + below]). This is because there is no way to automatically detect full + duplex links except through autonegotiation. When I include the + autonegotiation feature in the SROM autoconf code, this detection will + occur automatically for that case. + + Command line arguments are now allowed, similar to passing arguments + through LILO. This will allow a per adapter board set up of full duplex + and media. The only lexical constraints are: the board name (dev->name) + appears in the list before its parameters. The list of parameters ends + either at the end of the parameter list or with another board name. The + following parameters are allowed: + + fdx for full duplex + autosense to set the media/speed; with the following + sub-parameters: + TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + + Case sensitivity is important for the sub-parameters. They *must* be + upper case. Examples: + + insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + + For a compiled in driver, in linux/drivers/net/CONFIG, place e.g. + DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' + + Yes, I know full duplex isn't permissible on BNC or AUI; they're just + examples. By default, full duplex is turned off and AUTO is the default + autosense setting. In reality, I expect only the full duplex option to + be used. Note the use of single quotes in the two examples above and the + lack of commas to separate items. + TO DO: ------ + o check what revision numbers the 21142 and 21143 have + o Revision History ---------------- @@ -300,11 +345,38 @@ Added byte counters from Added SA_INTERRUPT temporary fix from . + 0.53 12-Nov-97 Fix the *_probe() to include 'eth??' name during + module load: bug reported by + + Fix multi-MAC, one SROM, to work with 2114x chips: + bug reported by . + Make above search independent of BIOS device scan + direction. + Completed DC2114[23] autosense functions. + 0.531 21-Dec-97 Fix DE500-XA 100Mb/s bug reported by + and + . + Added argument list to set up each board from either + a module's command line or a compiled in #define. + Added generic MII PHY functionality to deal with + newer PHY chips. + Fix the mess in 2.1.67. + 0.532 5-Jan-98 Fix bug in mii_get_phy() reported by + . + Fix bug in pci_probe() for 64 bit systems reported + by . + 0.533 9-Jan-98 Fix more 64 bit bugs reported by . + 0.534 24-Jan-98 Fix last (?) endian bug from + + 0.535 21-Feb-98 Fix Ethernet Address PROM reset bug for DC21040. ========================================================================= */ -static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.535 1998/2/21 davies@maniac.ultranet.com\n"; #include @@ -339,25 +411,28 @@ #define c_char const char #include -#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8)) -#define net_device_stats enet_statistics -#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) -#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) -#define le16_to_cpu(a) cpu_to_le16(a) -#define le32_to_cpu(a) cpu_to_le32(a) -#ifdef __powerpc__ -#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) -#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ - (((a) & 0x0000ff00U) << 8) |\ - (((a) & 0x00ff0000U) >> 8) |\ - (((a) & 0xff000000U) >> 24)) +#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) +# define __initfunc(__arginit) __arginit +# define test_and_set_bit set_bit +# define net_device_stats enet_statistics +# define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +# define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +# define le16_to_cpu(a) cpu_to_le16(a) +# define le32_to_cpu(a) cpu_to_le32(a) +# ifdef __powerpc__ +# define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) +# define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ + (((a) & 0x0000ff00U) << 8) |\ + (((a) & 0x00ff0000U) >> 8) |\ + (((a) & 0xff000000U) >> 24)) +# else +# define cpu_to_le16(a) (a) +# define cpu_to_le32(a) (a) +# endif /* __powerpc__ */ +# include #else -#define cpu_to_le16(a) (a) -#define cpu_to_le32(a) (a) -#endif /* __powerpc__ */ -#include -#else -#include +# include +# include #endif /* LINUX_VERSION_CODE */ #define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) @@ -391,6 +466,7 @@ u_int ana; /* NWay Advertisement */ u_int fdx; /* Full DupleX capabilites for each media */ u_int ttm; /* Transmit Threshold Mode for each media */ + u_int mci; /* 21142 MII Connector Interrupt info */ }; #define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ @@ -407,16 +483,26 @@ /* ** Define the know universe of PHY devices that can be -** recognised by this driver +** recognised by this driver. */ static struct phy_table phy_info[] = { - {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ - {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ + {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ + {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}, /* Cypress T4 */ + {0, 0x7810 , 1, {0x05, 0x0380, 0x0380}} /* Level One? */ }; /* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG 0x05 /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK MII_ANLPA_100M /* All 100Mb/s Technologies */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4 */ + +/* ** Define special SROM detection cases */ static c_char enet_det[][ETH_ALEN] = { @@ -443,21 +529,31 @@ #ifdef DE4X5_DEBUG static int de4x5_debug = DE4X5_DEBUG; #else -static int de4x5_debug = (0); +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); #endif -#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */ -static int de4x5_autosense = DE4X5_AUTOSENSE; +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.: +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' +** in linux/drivers/net/CONFIG +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; #else -static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */ +static char *args = NULL; #endif -#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ -#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */ -static s32 de4x5_full_duplex = 1; -#else -static s32 de4x5_full_duplex = 0; -#endif +struct parameters { + int fdx; + int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250 /* msec autosense tick (DE500) */ #define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */ @@ -497,11 +593,18 @@ #define DE4X5_NAME_LENGTH 8 /* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH 32 +#define ETH_PROM_SIG 0xAA5500FFUL + +/* ** PCI Bus defines */ #define PCI_MAX_BUS_NUM 8 #define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */ #define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */ +#define NO_MORE_PCI -2 /* PCI bus search all done */ /* ** Memory Alignment. Each descriptor is 4 longwords long. To force a @@ -674,7 +777,7 @@ struct { void *priv; /* Original kmalloc'd mem addr */ void *buf; /* Original kmalloc'd mem addr */ - int lock; /* Lock the cache accesses */ + u_long lock; /* Lock the cache accesses */ s32 csr0; /* Saved Bus Mode Register */ s32 csr6; /* Saved Operating Mode Reg. */ s32 csr7; /* Saved IRQ Mask Register */ @@ -703,6 +806,7 @@ int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */ u_char *rst; /* Pointer to Type 5 reset info */ u_char ibn; /* Infoblock number */ + struct parameters params; /* Command line/ #defined params */ }; /* @@ -784,11 +888,13 @@ static int dc21040_autoconf(struct device *dev); static int dc21041_autoconf(struct device *dev); static int dc21140m_autoconf(struct device *dev); +static int dc2114x_autoconf(struct device *dev); static int srom_autoconf(struct device *dev); static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *)); static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int)); static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); -static int test_sym_link(struct device *dev, int msec); +static int test_for_100Mb(struct device *dev, int msec); +static int wait_for_link(struct device *dev); static int test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec); static int is_spd_100(struct device *dev); static int is_100_up(struct device *dev); @@ -799,7 +905,7 @@ static void de4x5_free_rx_buffs(struct device *dev); static void de4x5_free_tx_buffs(struct device *dev); static void de4x5_save_skbs(struct device *dev); -static void de4x5_restore_skbs(struct device *dev); +static void de4x5_rst_desc_ring(struct device *dev); static void de4x5_cache_state(struct device *dev, int flag); static void de4x5_put_cache(struct device *dev, struct sk_buff *skb); static void de4x5_putb_cache(struct device *dev, struct sk_buff *skb); @@ -813,6 +919,7 @@ static int EISA_signature(char *name, s32 eisa_id); static int PCI_signature(char *name, struct bus_type *lp); static void DevicePresent(u_long iobase); +static void enet_addr_rst(u_long aprom_addr); static int de4x5_bad_srom(struct bus_type *lp); static short srom_rd(u_long address, u_char offset); static void srom_latch(u_int command, u_long address); @@ -822,7 +929,7 @@ /*static void srom_busy(u_int command, u_long address);*/ static void sendto_srom(u_int command, u_long addr); static int getfrom_srom(u_long addr); -static void srom_map_media(struct device *dev); +static int srom_map_media(struct device *dev); static int srom_infoleaf_info(struct device *dev); static void srom_init(struct device *dev); static void srom_exec(struct device *dev, u_char *p); @@ -841,20 +948,21 @@ static int get_hw_addr(struct device *dev); static void srom_repair(struct device *dev, int card); static int test_bad_enet(struct device *dev, int status); - +#ifndef __sparc_v9__ static void eisa_probe(struct device *dev, u_long iobase); +#endif static void pci_probe(struct device *dev, u_long iobase); -static struct device *alloc_device(struct device *dev, u_long iobase); -static struct device *insert_device(struct device *dev, u_long iobase, - int (*init)(struct device *)); +static void srom_search(int index); static char *build_setup_frame(struct device *dev, int mode); static void disable_ast(struct device *dev); static void enable_ast(struct device *dev, u32 time_out); static long de4x5_switch_mac_port(struct device *dev); +static int gep_rd(struct device *dev); +static void gep_wr(s32 data, struct device *dev); static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); static void yawn(struct device *dev, int state); -static int de4x5_dev_index(char *s); static void link_modules(struct device *dev, struct device *tmp); +static void de4x5_parse_params(struct device *dev); static void de4x5_dbg_open(struct device *dev); static void de4x5_dbg_mii(struct device *dev, int k); static void de4x5_dbg_media(struct device *dev); @@ -877,15 +985,27 @@ int init_module(void); void cleanup_module(void); static struct device *unlink_modules(struct device *p); -static int autoprobed = 0, loading_module = 1; +static struct device *insert_device(struct device *dev, u_long iobase, + int (*init)(struct device *)); +static int count_adapters(void); +static int loading_module = 1; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(de4x5_debug, "i"); +MODULE_PARM(dec_only, "i"); +MODULE_PARM(args, "s"); +#endif /* LINUX_VERSION_CODE */ # else -static int autoprobed = 0, loading_module = 0; +static int loading_module = 0; #endif /* MODULE */ static char name[DE4X5_NAME_LENGTH + 1]; +#ifndef __sparc_v9__ static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; -static int num_de4x5s = 0, num_eth = 0; +#endif +static int num_de4x5s = 0; static int cfrv = 0, useSROM = 0; +static int lastEISA = 0, lastPCI = -1; +static struct device *lastModule = NULL; /* ** List the SROM infoleaf functions and chipsets @@ -942,32 +1062,23 @@ /* ** Autoprobing in modules is allowed here. See the top of the file for -** more info. Until I fix (un)register_netdevice() we won't be able to use it -** though. +** more info. */ -int -de4x5_probe(struct device *dev) +__initfunc(int +de4x5_probe(struct device *dev)) { - int status = -ENODEV; u_long iobase = dev->base_addr; +#ifndef __sparc_v9__ eisa_probe(dev, iobase); +#endif pci_probe(dev, iobase); - /* - ** Walk the device list to check that at least one device - ** initialised OK - */ - for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); - - if (dev->priv) status = 0; - if (iobase == 0) autoprobed = 1; - - return status; + return (dev->priv ? 0 : -ENODEV); } -static int -de4x5_hw_init(struct device *dev, u_long iobase) +__initfunc(static int +de4x5_hw_init(struct device *dev, u_long iobase)) { struct bus_type *lp = &bus; int i, status=0; @@ -1053,33 +1164,34 @@ lp->timeout = -1; lp->useSROM = useSROM; memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); + de4x5_parse_params(dev); /* ** Choose correct autosensing in case someone messed up */ - if ((de4x5_autosense & AUTO) || lp->useSROM) { + if ((lp->params.autosense & AUTO) || lp->useSROM) { lp->autosense = AUTO; } else { if (lp->chipset != DC21140) { - if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) { - de4x5_autosense = TP; + if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { + lp->params.autosense = TP; } - if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) { - de4x5_autosense = BNC; + if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { + lp->params.autosense = BNC; } - lp->autosense = de4x5_autosense & 0x001f; + lp->autosense = lp->params.autosense & 0x001f; } else { - lp->autosense = de4x5_autosense & 0x00c0; + lp->autosense = lp->params.autosense & 0x00c0; } } - lp->fdx = de4x5_full_duplex; + lp->fdx = lp->params.fdx; sprintf(lp->adapter_name,"%s (%s)", name, dev->name); /* ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) for (i=0; irx_ring[i].status = 0; lp->rx_ring[i].des1 = RX_BUFF_SZ; @@ -1139,7 +1251,6 @@ /* Initialise the SROM pointers if possible */ if (lp->useSROM) { lp->state = INITIALISED; - de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); if (srom_infoleaf_info(dev)) { return -ENXIO; } @@ -1155,7 +1266,11 @@ mii_get_phy(dev); } +#ifndef __sparc_v9__ printk(" and requires IRQ%d (provided by %s).\n", dev->irq, +#else + printk(" and requires IRQ%x (provided by %s).\n", dev->irq, +#endif ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); } @@ -1190,7 +1305,7 @@ u_long iobase = dev->base_addr; int i, status = 0; s32 omr; - + /* Allocate the RX buffers */ for (i=0; irxRingSize; i++) { if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { @@ -1229,6 +1344,7 @@ printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); } } + dev->tbusy = 0; dev->start = 1; dev->interrupt = UNMASK_INTERRUPTS; @@ -1266,7 +1382,7 @@ de4x5_init(struct device *dev) { /* Lock out other processes whilst setting up the hardware */ - set_bit(0, (void *)&dev->tbusy); + test_and_set_bit(0, (void *)&dev->tbusy); de4x5_sw_reset(dev); @@ -1287,9 +1403,9 @@ /* Select the MII or SRL port now and RESET the MAC */ if (!lp->useSROM) { if (lp->phy[lp->active].id != 0) { - lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; } else { - lp->infoblock_csr6 = OMR_TTM; + lp->infoblock_csr6 = OMR_SDP | OMR_TTM; } de4x5_switch_mac_port(dev); } @@ -1300,8 +1416,9 @@ ** without these values. Cache align 16 long. */ bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN; + bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); outl(bmr, DE4X5_BMR); - + omr = inl(DE4X5_OMR) & ~OMR_PR; /* Turn off promiscuous mode */ if (lp->chipset == DC21140) { omr |= (OMR_SDP | OMR_SB); @@ -1322,13 +1439,13 @@ } barrier(); - + /* Build the setup frame depending on filtering mode */ SetMulticastFilter(dev); load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL); outl(omr|OMR_ST, DE4X5_OMR); - + /* Poll for setup frame completion (adapter interrupts are disabled now) */ sti(); /* Ensure timer interrupts */ for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ @@ -1336,7 +1453,7 @@ if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; } outl(omr, DE4X5_OMR); /* Stop everything! */ - + if (j == 0) { printk("%s: Setup frame timed out, status %08x\n", dev->name, inl(DE4X5_STS)); @@ -1345,7 +1462,7 @@ lp->tx_new = (++lp->tx_new) % lp->txRingSize; lp->tx_old = lp->tx_new; - + return status; } @@ -1359,12 +1476,7 @@ u_long iobase = dev->base_addr; int status = 0; - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */ + test_and_set_bit(0, (void*)&dev->tbusy); /* Stop send re-tries */ if (lp->tx_enable == NO) { /* Cannot send for now */ return -1; } @@ -1379,7 +1491,8 @@ sti(); /* Test if cache is already locked - requeue skb if so */ - if (set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1; + if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) + return -1; /* Transmit descriptor ring full or stale skb */ if (dev->tbusy || lp->tx_skb[lp->tx_new]) { @@ -1400,7 +1513,7 @@ while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) { cli(); - set_bit(0, (void*)&dev->tbusy); + test_and_set_bit(0, (void*)&dev->tbusy); load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); #if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) lp->stats.tx_bytes += skb->len; @@ -1488,7 +1601,7 @@ } /* Load the TX ring with any locally stored packets */ - if (!set_bit(0, (void *)&lp->cache.lock)) { + if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { while (lp->cache.skb && !dev->tbusy && lp->tx_enable) { de4x5_queue_pkt(de4x5_get_cache(dev), dev); } @@ -1660,7 +1773,7 @@ de4x5_txur(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int omr; omr = inl(DE4X5_OMR); @@ -1683,7 +1796,7 @@ de4x5_rx_ovfc(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int omr; omr = inl(DE4X5_OMR); @@ -1888,23 +2001,24 @@ return; } +#ifndef __sparc_v9__ /* ** 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) +__initfunc(static void +eisa_probe(struct device *dev, u_long ioaddr)) { int i, maxSlots, status, device; + u_char irq; u_short vendor; u32 cfid; u_long iobase; struct bus_type *lp = &bus; char name[DE4X5_STRLEN]; - struct device *tmp; - - if (autoprobed) return; /* Been here before ! */ - + + if (lastEISA == MAX_EISA_SLOTS) return;/* No more EISA devices to search */ + lp->bus = EISA; if (ioaddr == 0) { /* Autoprobing */ @@ -1917,7 +2031,7 @@ maxSlots = i + 1; } - for (status= -ENODEV;(iirq = inb(EISA_REG0); - dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03]; + irq = inb(EISA_REG0); + irq = de4x5_irq[(irq >> 1) & 0x03]; - if (is_DC2114x) device |= (cfrv & 0x00f0); + if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - DevicePresent(DE4X5_APROM); + /* Write the PCI Configuration Registers */ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); outl(0x00006000, PCI_CFLT); outl(iobase, PCI_CBIO); + DevicePresent(EISA_APROM); if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) { - if ((tmp = alloc_device(dev, iobase)) != NULL) { - if ((status = de4x5_hw_init(tmp, iobase)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(dev, tmp); - } else if (loading_module && (tmp != dev)) { - kfree(tmp); - } + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase)) == 0) { + num_de4x5s++; + if (loading_module) link_modules(lastModule, dev); + lastEISA = i; + return; } - } else if (autoprobed) { + } else if (ioaddr != 0) { printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase); } } } - + + if (ioaddr == 0) lastEISA = i; + return; } +#endif /* !(__sparc_v9__) */ /* ** PCI bus I/O device probe @@ -1969,23 +2086,25 @@ #define PCI_DEVICE (dev_num << 3) #define PCI_LAST_DEV 32 -static void -pci_probe(struct device *dev, u_long ioaddr) +__initfunc(static void +pci_probe(struct device *dev, u_long ioaddr)) { - u_char irq; - u_char pb, pbus, dev_num, dnum, dev_fn; + u_char pb, pbus, dev_num, dnum, dev_fn, timer, tirq; u_short dev_id, vendor, index, status; - u_int class = DE4X5_CLASS_CODE; - u_int device, iobase; + u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ struct bus_type *lp = &bus; - struct device *tmp; - if (autoprobed) return; - - if (!pcibios_present()) return; /* No PCI bus in this machine! */ + if (lastPCI == NO_MORE_PCI) return; + + if (!pcibios_present()) { + lastPCI = NO_MORE_PCI; + return; /* No PCI bus in this machine! */ + } lp->bus = PCI; - + lp->bus_num = 0; + if ((ioaddr < 0x1000) && loading_module) { pbus = (u_short)(ioaddr >> 8); dnum = (u_short)(ioaddr & 0xff); @@ -1994,12 +2113,17 @@ dnum = 0; } - for (index=0; + for (index=lastPCI+1; (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); - if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { +#ifdef __sparc_v9__ + struct pci_dev *pdev; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; + } +#endif device = 0; pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); @@ -2009,6 +2133,12 @@ continue; } + /* Search for an SROM on this bus */ + if (lp->bus_num != pb) { + lp->bus_num = pb; + srom_search(index); + } + /* Get the chip configuration revision register */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); @@ -2017,25 +2147,38 @@ lp->bus_num = pb; /* Set the chipset information */ - if (is_DC2114x) device |= (cfrv & 0x00f0); + if (is_DC2114x) device |= (cfrv & CFRV_RN); lp->chipset = device; - if (is_DC21142 || is_DC21143) { - printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143")); - continue; - } - - /* Get the board I/O address */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); + /* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); + iobase = tmp; +#else + iobase = pdev->base_address[0]; +#endif iobase &= CBIO_MASK; /* Fetch the IRQ to be used */ - pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq); - if ((irq == 0) || (irq == (u_char) 0xff)) continue; +#ifndef __sparc_v9__ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); + irq = tirq; +#else + irq = pdev->irq; +#endif + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; /* Check if I/O accesses and Bus Mastering are enabled */ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +#ifdef __powerpc__ + if (!(status & PCI_COMMAND_IO)) { + status |= PCI_COMMAND_IO; + pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + } +#endif /* __powerpc__ */ if (!(status & PCI_COMMAND_IO)) continue; + if (!(status & PCI_COMMAND_MASTER)) { status |= PCI_COMMAND_MASTER; pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); @@ -2043,131 +2186,126 @@ } if (!(status & PCI_COMMAND_MASTER)) continue; + /* Check the latency timer for values >= 0x60 */ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer); + if (timer < 0x60) { + pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60); + } + DevicePresent(DE4X5_APROM); if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { - if ((tmp = alloc_device(dev, iobase)) != NULL) { - tmp->irq = irq; - if ((status = de4x5_hw_init(tmp, iobase)) == 0) { - num_de4x5s++; - if (loading_module) link_modules(dev, tmp); - } else if (loading_module && (tmp != dev)) { - kfree(tmp); + dev->irq = irq; + if ((status = de4x5_hw_init(dev, iobase)) == 0) { + num_de4x5s++; + if (loading_module) { + link_modules(lastModule, dev); + lastPCI = index; } + return; } - } else if (autoprobed) { - printk("%s: region already allocated at 0x%04x.\n", dev->name, - (u_short)iobase); + } else if (ioaddr != 0) { + printk("%s: region already allocated at 0x%04lx.\n", dev->name, + iobase); } } } - + + if (loading_module) lastPCI = NO_MORE_PCI; + return; } /* -** Search the entire 'eth' device list for a fixed probe. If a match isn't -** found then check for an autoprobe or unused device location. If they -** are not available then insert a new device structure at the end of -** the current list. +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... */ -static struct device * -alloc_device(struct device *dev, u_long iobase) +__initfunc(static void +srom_search(int index)) { - struct device *adev = NULL; - int fixed = 0, new_dev = 0; + u_char pb, dev_fn, tirq; + u_short dev_id, dev_num, vendor, status; + u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; + u_long iobase = 0; /* Clear upper 32 bits in Alphas */ + int i, j; + struct bus_type *lp = &bus; - if (!dev) return dev; - num_eth = de4x5_dev_index(dev->name); + for (; + (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); + index++) { - if (loading_module) { - if (dev->priv) { - dev = insert_device(dev, iobase, de4x5_probe); + if (lp->bus_num != pb) return; + dev_num = PCI_SLOT(dev_fn); +#ifdef __sparc_v9__ + struct pci_dev *pdev; + for (pdev = pci_devices; pdev; pdev = pdev->next) { + if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break; } - num_eth++; - return dev; - } - - while (1) { - if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) { - adev=dev; - } else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { - fixed = 1; - } else { - if (dev->next == NULL) { - new_dev = 1; - } else if (strncmp(dev->next->name, "eth", 3) != 0) { - new_dev = 1; - } +#endif + device = 0; + pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); + device = dev_id; + device <<= 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { + continue; } - if ((dev->next == NULL) || new_dev || fixed) break; - dev = dev->next; - num_eth++; - } - if (adev && !fixed) { - dev = adev; - num_eth = de4x5_dev_index(dev->name); - new_dev = 0; - } - - if (((dev->next == NULL) && - ((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) || - new_dev) { - num_eth++; /* New device */ - dev = insert_device(dev, iobase, de4x5_probe); - } - - return dev; -} -/* -** If at end of eth device list and can't use current entry, malloc -** one up. If memory could not be allocated, print an error message. -*/ -static struct device * -insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)) -{ - struct device *new; + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + + /* Set the device number information */ + lp->device = dev_num; + lp->bus_num = pb; + + /* Set the chipset information */ + if (is_DC2114x) device |= (cfrv & CFRV_RN); + lp->chipset = device; + + /* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); + iobase = tmp; +#else + iobase = pdev->base_address[0]; +#endif + iobase &= CBIO_MASK; - new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); - if (new == NULL) { - printk("eth%d: Device not initialised, insufficient memory\n",num_eth); - return NULL; - } else { - memset((char *)new, 0, sizeof(struct device)+8); - new->name = (char *)(new + 1); - new->base_addr = iobase; /* assign the io address */ - new->init = init; /* initialisation routine */ - if (!loading_module) { - new->next = dev->next; - dev->next = new; - if (num_eth > 9999) { - sprintf(new->name,"eth????");/* New device name */ - } else { - sprintf(new->name,"eth%d", num_eth);/* New device name */ + /* Fetch the IRQ to be used */ +#ifndef __sparc_v9__ + pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); + irq = tirq; +#else + irq = pdev->irq; +#endif + if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; + + /* Check if I/O accesses are enabled */ + pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); + if (!(status & PCI_COMMAND_IO)) continue; + + /* Search for a valid SROM attached to this DECchip */ + DevicePresent(DE4X5_APROM); + for (j=0, i=0; isrom + SROM_HWADD + i); + } + if ((j != 0) && (j != 0x5fa)) { + last.chipset = device; + last.bus = pb; + last.irq = irq; + for (i=0; isrom + SROM_HWADD + i); } + return; } } - return new; -} - -static int -de4x5_dev_index(char *s) -{ - int i=0, j=0; - - for (;*s; s++) { - if (isdigit(*s)) { - j=1; - i = (i * 10) + (*s - '0'); - } else if (j) break; - } - - return i; + return; } -static void -link_modules(struct device *dev, struct device *tmp) +__initfunc(static void +link_modules(struct device *dev, struct device *tmp)) { struct device *p=dev; @@ -2198,13 +2336,15 @@ { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - int next_tick = DE4X5_AUTOSENSE_MS;; - + int next_tick = DE4X5_AUTOSENSE_MS; + lp->linkOK = 0; lp->c_media = AUTO; /* Bogus last media */ disable_ast(dev); inl(DE4X5_MFC); /* Zero the lost frames counter */ lp->media = INIT; + lp->tcount = 0; + if (lp->useSROM) { next_tick = srom_autoconf(dev); } else if (lp->chipset == DC21040) { @@ -2592,9 +2732,9 @@ dc21140m_autoconf(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr; + int ana, anlpa, cap, cr, slnk, sr; int next_tick = DE4X5_AUTOSENSE_MS; - u_long imr, omr; + u_long imr, omr, iobase = dev->base_addr; switch(lp->media) { case INIT: @@ -2608,7 +2748,10 @@ next_tick &= ~TIMER_CB; } else { if (lp->useSROM) { - srom_map_media(dev); + if (srom_map_media(dev) < 0) { + lp->tcount++; + return next_tick; + } srom_exec(dev, lp->phy[lp->active].gep); if (lp->infoblock_media == ANS) { ana = lp->phy[lp->active].ana | MII_ANA_CSMA; @@ -2690,11 +2833,11 @@ case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ if (lp->timeout < 0) { - lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : - (~inl(DE4X5_GEP) & GEP_LNP)); + lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : + (~gep_rd(dev) & GEP_LNP)); SET_100Mb_PDET; } - if ((slnk = test_sym_link(dev, 6200)) < 0) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { next_tick = slnk & ~TIMER_CB; } else { if (is_spd_100(dev) && is_100_up(dev)) { @@ -2715,7 +2858,7 @@ de4x5_init_connection(dev); } else { if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!(is_spd_100(dev) && is_100_up(dev))) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { lp->media = INIT; lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; @@ -2731,7 +2874,7 @@ de4x5_init_connection(dev); } else { if (!lp->linkOK && (lp->autosense == AUTO)) { - if (!(!is_spd_100(dev) && is_10_up(dev))) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { lp->media = INIT; lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; @@ -2753,96 +2896,343 @@ return next_tick; } -static int -srom_autoconf(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - - return lp->infoleaf_fn(dev); -} - /* -** This mapping keeps the original media codes and FDX flag unchanged. -** While it isn't strictly necessary, it helps me for the moment... +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. */ -static void -srom_map_media(struct device *dev) +static int +dc2114x_autoconf(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; + int next_tick = DE4X5_AUTOSENSE_MS; - lp->fdx = 0; - switch(lp->infoblock_media) { - case SROM_10BASETF: - lp->fdx = TRUE; - case SROM_10BASET: - if (lp->chipset == DC21140) { - lp->media = _10Mb; - } else { - lp->media = TP; + switch (lp->media) { + case INIT: + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + lp->timeout = -1; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ } + if ((next_tick = de4x5_reset_phy(dev)) < 0) { + next_tick &= ~TIMER_CB; + } else { + lp->media = SPD_DET; + if ((lp->infoblock_media == ANS) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } + lp->local_state = 0; + next_tick = dc2114x_autoconf(dev); + } break; - - case SROM_10BASE2: - lp->media = BNC; - break; - - case SROM_10BASE5: - lp->media = AUI; - break; - - case SROM_100BASETF: - lp->fdx = TRUE; - case SROM_100BASET: - lp->media = _100Mb; - break; - - case SROM_100BASET4: - lp->media = _100Mb; - break; - - case SROM_100BASEFF: - lp->fdx = TRUE; - case SROM_100BASEF: - lp->media = _100Mb; - break; - + case ANS: - lp->media = ANS; - break; - - default: - printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, - lp->infoblock_media); - break; - } - - return; -} - -static void -de4x5_init_connection(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - - if (lp->media != lp->c_media) { - de4x5_dbg_media(dev); - lp->c_media = lp->media; /* Stop scrolling media messages */ - } - - cli(); - de4x5_restore_skbs(dev); - de4x5_setup_intr(dev); - lp->tx_enable = YES; - dev->tbusy = 0; - sti(); - outl(POLL_DEMAND, DE4X5_TPD); - mark_bh(NET_BH); - - return; -} - -/* + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); + if (cr < 0) { + next_tick = cr & ~TIMER_CB; + } else { + if (cr) { + lp->local_state = 0; + lp->media = SPD_DET; + } else { + lp->local_state++; + } + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { + next_tick = sr & ~TIMER_CB; + } else { + lp->media = SPD_DET; + lp->local_state = 0; + if (sr) { /* Success! */ + lp->tmp = MII_SR_ASSC; + anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); + ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + if (!(anlpa & MII_ANLPA_RF) && + (cap = anlpa & MII_ANLPA_TAF & ana)) { + if (cap & MII_ANA_100M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->media = _100Mb; + } else if (cap & MII_ANA_10M) { + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->media = _10Mb; + } + } + } /* Auto Negotiation failed to finish */ + next_tick = dc2114x_autoconf(dev); + } /* Auto Negotiation failed to start */ + break; + } + break; + + case AUI: + if (!lp->tx_enable) { + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { + lp->media = BNC; + next_tick = dc2114x_autoconf(dev); + } else { + lp->local_state = 1; + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = AUI_SUSPECT; + next_tick = 3000; + } + break; + + case AUI_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); + break; + + case BNC: + switch (lp->local_state) { + case 0: + if (lp->timeout < 0) { + omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ + outl(omr & ~OMR_FDX, DE4X5_OMR); + } + irqs = 0; + irq_mask = 0; + sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); + if (sts < 0) { + next_tick = sts & ~TIMER_CB; + } else { + lp->local_state++; /* Ensure media connected */ + next_tick = dc2114x_autoconf(dev); + } + break; + + case 1: + if (!lp->tx_enable) { + if ((sts = ping_media(dev, 3000)) < 0) { + next_tick = sts & ~TIMER_CB; + } else { + if (sts) { + lp->local_state = 0; + lp->tcount++; + lp->media = INIT; + } else { + de4x5_init_connection(dev); + } + } + } else if (!lp->linkOK && (lp->autosense == AUTO)) { + lp->media = BNC_SUSPECT; + next_tick = 3000; + } + break; + } + break; + + case BNC_SUSPECT: + next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); + break; + + case SPD_DET: /* Choose 10Mb/s or 100Mb/s */ + if (srom_map_media(dev) < 0) { + lp->tcount++; + lp->media = INIT; + return next_tick; + } + if (lp->media == _100Mb) { + if ((slnk = test_for_100Mb(dev, 6500)) < 0) { + lp->media = SPD_DET; + return (slnk & ~TIMER_CB); + } + } else { + if (wait_for_link(dev) < 0) { + lp->media = SPD_DET; + return PDET_LINK_WAIT; + } + } + if (((lp->media == _100Mb) && is_100_up(dev)) || + ((lp->media == _10Mb) && is_10_up(dev)) || + (lp->media == BNC) || (lp->media == AUI)) { + next_tick = dc2114x_autoconf(dev); + } else { + lp->tcount++; + lp->media = INIT; + } + break; + + case _10Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_10Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + case _100Mb: + next_tick = 3000; + if (!lp->tx_enable) { + SET_100Mb; + de4x5_init_connection(dev); + } else { + if (!lp->linkOK && (lp->autosense == AUTO)) { + if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { + lp->media = INIT; + lp->tcount++; + next_tick = DE4X5_AUTOSENSE_MS; + } + } + } + break; + + default: + lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); + lp->media = INIT; + break; + } + + return next_tick; +} + +static int +srom_autoconf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + if (lp->infoblock_media == lp->media) + return 0; + + switch(lp->infoblock_media) { + case SROM_10BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_10BASET: + if (lp->params.fdx && !lp->fdx) return -1; + if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASET: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + if (!lp->params.fdx) return -1; + lp->fdx = TRUE; + case SROM_100BASEF: + if (lp->params.fdx && !lp->fdx) return -1; + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + return -1; + break; + } + + return 0; +} + +static void +de4x5_init_connection(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; /* Stop scrolling media messages */ + } + + cli(); + de4x5_rst_desc_ring(dev); + de4x5_setup_intr(dev); + lp->tx_enable = YES; + dev->tbusy = 0; + sti(); + outl(POLL_DEMAND, DE4X5_TPD); + mark_bh(NET_BH); + + return; +} + +/* ** General PHY reset function. Some MII devices don't reset correctly ** since their MII address pins can float at voltages that are dependent ** on the signal pin use. Do a double reset to ensure a reset. @@ -2857,7 +3247,7 @@ if ((lp->useSROM) || (lp->phy[lp->active].id)) { if (lp->timeout < 0) { if (lp->useSROM) { - if (lp->phy[lp->active].rst) { /* MII device specific reset */ + if (lp->phy[lp->active].rst) { srom_exec(dev, lp->phy[lp->active].rst); srom_exec(dev, lp->phy[lp->active].rst); } else if (lp->rst) { /* Type 5 infoblock reset */ @@ -2890,7 +3280,9 @@ if (lp->timeout < 0) { lp->timeout = msec/100; - reset_init_sia(dev, csr13, csr14, csr15); + if (!lp->useSROM) { /* Already done if by SROM, else dc2104[01] */ + reset_init_sia(dev, csr13, csr14, csr15); + } /* set up the interrupt mask */ outl(irq_mask, DE4X5_IMR); @@ -2900,7 +3292,7 @@ outl(sts, DE4X5_STS); /* clear csr12 NRA and SRA bits */ - if (lp->chipset == DC21041) { + if ((lp->chipset == DC21041) || lp->useSROM) { csr12 = inl(DE4X5_SISR); outl(csr12, DE4X5_SISR); } @@ -2939,24 +3331,37 @@ return sisr; } +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500 /* ms */ +#define SAMPLE_DELAY 2000 /* ms */ static int -test_sym_link(struct device *dev, int msec) +test_for_100Mb(struct device *dev, int msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; - int gep = 0; - + int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + if (lp->timeout < 0) { - lp->timeout = msec/100; + if ((msec/SAMPLE_INTERVAL) <= 0) return 0; + if (msec > SAMPLE_DELAY) { + lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; + gep = SAMPLE_DELAY | TIMER_CB; + return gep; + } else { + lp->timeout = msec/SAMPLE_INTERVAL; + } } if (lp->phy[lp->active].id || lp->useSROM) { - gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0); + gep = is_100_up(dev) | is_spd_100(dev); } else { - gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP)); + gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); } - if (!(gep & GEP_SLNK) && --lp->timeout) { - gep = 100 | TIMER_CB; + if (!(gep & ret) && --lp->timeout) { + gep = SAMPLE_INTERVAL | TIMER_CB; } else { lp->timeout = -1; } @@ -2964,6 +3369,24 @@ return gep; } +static int +wait_for_link(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + if (lp->timeout < 0) { + lp->timeout = 1; + } + + if (lp->timeout--) { + return TIMER_CB; + } else { + lp->timeout = -1; + } + + return 0; +} + /* ** ** @@ -2972,7 +3395,8 @@ test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int test, iobase = dev->base_addr; + int test; + u_long iobase = dev->base_addr; if (lp->timeout < 0) { lp->timeout = msec/100; @@ -2998,16 +3422,18 @@ u_long iobase = dev->base_addr; int spd; - if (lp->useSROM && !lp->useMII) { - spd = (lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); spd = ~(spd ^ lp->phy[lp->active].spd.value); spd &= lp->phy[lp->active].spd.mask; + } else if (!lp->useSROM) { /* de500-xa */ + spd = ((~gep_rd(dev)) & GEP_SLNK); } else { - spd = ((~inl(DE4X5_GEP)) & GEP_SLNK); + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); } return spd; @@ -3019,16 +3445,18 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->useSROM && !lp->useMII) { - return ((lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_SLNK); } else { - return ((~inl(DE4X5_GEP)) & GEP_SLNK); + if ((lp->ibn == 2) || !lp->asBitValid) + return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); } } @@ -3038,16 +3466,20 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->useSROM && !lp->useMII) { - return ((lp->asBitValid & - (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | - (lp->linkOK & ~lp->asBitValid)); - } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { + if (lp->useMII) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); + } else if (!lp->useSROM) { /* de500-xa */ + return ((~gep_rd(dev)) & GEP_LNP); } else { - return ((~inl(DE4X5_GEP)) & GEP_LNP); + if ((lp->ibn == 2) || !lp->asBitValid) + return (((lp->chipset & ~0x00ff) == DC2114x) ? + (~inl(DE4X5_SISR)&SISR_LS10): + 0); + + return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); } } @@ -3059,6 +3491,8 @@ if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SISR) & SISR_LPN) >> 11; } else { return 0; } @@ -3115,7 +3549,7 @@ struct de4x5_private *lp = (struct de4x5_private *)dev->priv; struct sk_buff *p; -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; @@ -3228,7 +3662,7 @@ } static void -de4x5_restore_skbs(struct device *dev) +de4x5_rst_desc_ring(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; @@ -3270,11 +3704,6 @@ lp->cache.csr0 = inl(DE4X5_BMR); lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); lp->cache.csr7 = inl(DE4X5_IMR); - if (lp->chipset != DC21140) { - lp->cache.csr13 = inl(DE4X5_SICR); - lp->cache.csr14 = inl(DE4X5_STRR); - lp->cache.csr15 = inl(DE4X5_SIGR); - } break; case DE4X5_RESTORE_STATE: @@ -3282,8 +3711,8 @@ outl(lp->cache.csr6, DE4X5_OMR); outl(lp->cache.csr7, DE4X5_IMR); if (lp->chipset == DC21140) { - outl(lp->cache.gepc, DE4X5_GEP); - outl(lp->cache.gep, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); } else { reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); @@ -3391,15 +3820,32 @@ ** */ static void -reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr) +reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - + RESET_SIA; - outl(sigr, DE4X5_SIGR); - outl(strr, DE4X5_STRR); - outl(sicr, DE4X5_SICR); + if (lp->useSROM) { + if (lp->ibn == 3) { + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].gep); + outl(1, DE4X5_SICR); + return; + } else { + csr15 = lp->cache.csr15; + csr14 = lp->cache.csr14; + csr13 = lp->cache.csr13; + outl(csr15 | lp->cache.gepc, DE4X5_SIGR); + outl(csr15 | lp->cache.gep, DE4X5_SIGR); + } + } else { + outl(csr15, DE4X5_SIGR); + } + outl(csr14, DE4X5_STRR); + outl(csr13, DE4X5_SICR); + + de4x5_ms_delay(10); return; } @@ -3522,6 +3968,8 @@ if (lp->chipset != DC21041) { useSROM = TRUE; /* card is not recognisably DEC */ } + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + useSROM = TRUE; } return status; @@ -3530,22 +3978,79 @@ /* ** Set up the Ethernet PROM counter to the start of the Ethernet address on ** the DC21040, else read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). */ static void DevicePresent(u_long aprom_addr) { - int i; - struct bus_type *lp = &bus; + int i, j=0; + struct bus_type *lp = &bus; + + if (lp->chipset == DC21040) { + if (lp->bus == EISA) { + enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } else { + outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ + } + } else { /* Read new srom */ + u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); + for (i=0; i<(ETH_ALEN>>1); i++) { + tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); + *p = le16_to_cpu(tmp); + j += *p++; + } + if ((j == 0) || (j == 0x2fffd)) { + return; + } + + p=(short *)&lp->srom; + for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { + tmp = srom_rd(aprom_addr, i); + *p++ = le16_to_cpu(tmp); + } + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + } + + return; +} + +/* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ + union { + struct { + u32 a; + u32 b; + } llsig; + char Sig[sizeof(u32) << 1]; + } dev; + short sigLength=0; + s8 data; + int i, j; - if (lp->chipset == DC21040) { - outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ - } else { /* Read new srom */ - u_short tmp, *p = (short *)&lp->srom; - for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { - tmp = srom_rd(aprom_addr, i); - *p++ = le16_to_cpu(tmp); + dev.llsig.a = ETH_PROM_SIG; + dev.llsig.b = ETH_PROM_SIG; + sigLength = sizeof(u32) << 1; + + for (i=0,j=0;jsrom); } return; @@ -3566,6 +4071,7 @@ struct bus_type *lp = &bus; broken = de4x5_bad_srom(lp); + for (i=0,k=0,j=0;j<3;j++) { k <<= 1; if (k > 0xffff) k-=0xffff; @@ -3672,6 +4178,10 @@ return; } +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ static int test_bad_enet(struct device *dev, int status) { @@ -3688,10 +4198,8 @@ if (dev->dev_addr[i] != 0) break; } for (i=0; idev_addr[i]; - if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) && - (lp->chipset == DC21040)) { - dev->irq = last.irq; - } + dev->irq = last.irq; + status = 0; } } else if (!status) { @@ -3752,9 +4260,6 @@ de4x5_us_delay(1); i = (getfrom_srom(addr) >> 3) & 0x01; - if (i != 0) { - printk("Bad SROM address phase.....\n"); - } return; } @@ -3867,14 +4372,13 @@ srom_init(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; u_char count; p+=2; if (lp->chipset == DC21140) { lp->cache.gepc = (*p++ | GEP_CTRL); - outl(lp->cache.gepc, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); } /* Block count */ @@ -3887,9 +4391,13 @@ } else if (*(p+1) == 5) { type5_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 4) { + p += ((*p & BLOCK_LEN) + 1); } else if (*(p+1) == 3) { type3_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 2) { + p += ((*p & BLOCK_LEN) + 1); } else if (*(p+1) == 1) { type1_infoblock(dev, 1, p); p += ((*p & BLOCK_LEN) + 1); @@ -3901,20 +4409,33 @@ return; } +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ static void srom_exec(struct device *dev, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; u_char count = (p ? *p++ : 0); + u_short *w = (u_short *)p; + + if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + if (lp->chipset != DC21140) RESET_SIA; + while (count--) { - if (lp->chipset == DC21140) { - outl(*p++, DE4X5_GEP); - } + gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? + *p++ : TWIDDLE(w++)), dev); udelay(2000); /* 2ms per action */ } + if (lp->chipset != DC21140) { + outl(lp->cache.csr14, DE4X5_STRR); + outl(lp->cache.csr13, DE4X5_SICR); + } + return; } @@ -3970,15 +4491,70 @@ static int dc21142_infoleaf(struct device *dev) { -printk("dc21142_infoleaf()\n"); - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; } static int dc21143_infoleaf(struct device *dev) { -printk("dc21143_infoleaf()\n"); - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; } /* @@ -3989,7 +4565,6 @@ compact_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char flags, csr6; /* Recursively figure out the info blocks */ @@ -4002,7 +4577,9 @@ } if ((lp->media == INIT) && (lp->timeout < 0)) { - outl(lp->cache.gepc, DE4X5_GEP); + lp->ibn = COMPACT; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); lp->infoblock_media = (*p++) & COMPACT_MC; lp->cache.gep = *p++; csr6 = *p++; @@ -4012,7 +4589,7 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); lp->useMII = FALSE; de4x5_switch_mac_port(dev); @@ -4023,12 +4600,11 @@ /* ** This block describes non MII media for the DC21140[A] only. - */ +*/ static int type0_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; u_char flags, csr6, len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ @@ -4041,7 +4617,9 @@ } if ((lp->media == INIT) && (lp->timeout < 0)) { - outl(lp->cache.gepc, DE4X5_GEP); + lp->ibn = 0; + lp->active = 0; + gep_wr(lp->cache.gepc, dev); p+=2; lp->infoblock_media = (*p++) & BLOCK0_MC; lp->cache.gep = *p++; @@ -4052,7 +4630,7 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); lp->useMII = FALSE; de4x5_switch_mac_port(dev); @@ -4080,7 +4658,6 @@ p += 2; if (lp->state == INITIALISED) { - lp->ibn = 1; lp->active = *p++; lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); @@ -4092,9 +4669,10 @@ } else if ((lp->media == INIT) && (lp->timeout < 0)) { lp->ibn = 1; lp->active = *p; - lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->infoblock_csr6 = OMR_MII_100; lp->useMII = TRUE; lp->infoblock_media = ANS; + de4x5_switch_mac_port(dev); } @@ -4104,14 +4682,49 @@ static int type2_infoblock(struct device *dev, u_char count, u_char *p) { - return DE4X5_AUTOSENSE_MS; + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 2; + lp->active = 0; + p += 2; + lp->infoblock_media = (*p) & MEDIA_CODE; + + if ((*p++) & EXT_FIELD) { + lp->cache.csr13 = TWIDDLE(p); p += 2; + lp->cache.csr14 = TWIDDLE(p); p += 2; + lp->cache.csr15 = TWIDDLE(p); p += 2; + } else { + lp->cache.csr13 = CSR13; + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + } + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); + lp->infoblock_csr6 = OMR_SIA; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); } static int type3_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + u_char len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ if (--count > lp->tcount) { @@ -4122,20 +4735,55 @@ } } + p += 2; if (lp->state == INITIALISED) { - lp->ibn = 3; p += 2; lp->active = *p++; - lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); - lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); lp->phy[lp->active].mc = TWIDDLE(p); p += 2; lp->phy[lp->active].ana = TWIDDLE(p); p += 2; lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; - lp->phy[lp->active].ttm = TWIDDLE(p); + lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; + lp->phy[lp->active].mci = *p; return 0; - } else if (lp->media == INIT) { + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 3; + lp->active = *p; + lp->infoblock_csr6 = OMR_MII_100; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + + de4x5_switch_mac_port(dev); + } + + return dc2114x_autoconf(dev); +} + +static int +type4_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 4; + lp->active = 0; p+=2; - lp->infoblock_media = (*p++) & BLOCK0_MC; - lp->cache.gep = *p++; + lp->infoblock_media = (*p++) & MEDIA_CODE; + lp->cache.csr13 = CSR13; /* Hard coded defaults */ + lp->cache.csr14 = CSR14; + lp->cache.csr15 = CSR15; + lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; + lp->cache.gep = ((s32)(TWIDDLE(p)) << 16); p += 2; csr6 = *p++; flags = *p++; @@ -4143,19 +4791,13 @@ lp->defMedium = (flags & 0x40) ? -1 : 0; lp->asBit = 1 << ((csr6 >> 1) & 0x07); lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; - lp->infoblock_csr6 = (csr6 & 0x71) << 18; - lp->useMII = TRUE; + lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); + lp->useMII = FALSE; de4x5_switch_mac_port(dev); } - return dc21140m_autoconf(dev); -} - -static int -type4_infoblock(struct device *dev, u_char count, u_char *p) -{ - return DE4X5_AUTOSENSE_MS; + return dc2114x_autoconf(dev); } /* @@ -4166,8 +4808,7 @@ type5_infoblock(struct device *dev, u_char count, u_char *p) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - u_long iobase = dev->base_addr; - u_char i, j, len = (*p & BLOCK_LEN)+1; + u_char len = (*p & BLOCK_LEN)+1; /* Recursively figure out the info blocks */ if (--count > lp->tcount) { @@ -4182,19 +4823,7 @@ if ((lp->state == INITIALISED) || (lp->media == INIT)) { p+=2; lp->rst = p; - i = *p++; - for (j=0;i;--i) { - if (lp->chipset == DC21140) { - if (!j) { - outl(*p++ | GEP_CTRL, DE4X5_GEP); - j++; - } - outl(*p++, DE4X5_GEP); - } else if (lp->chipset == DC21142) { - } else if (lp->chipset == DC21143) { - } - } - + srom_exec(dev, lp->rst); } return DE4X5_AUTOSENSE_MS; @@ -4326,8 +4955,7 @@ } /* -** Here's 3 ways to calculate the OUI from the ID registers. One's a brain -** dead approach, 2 aren't (clue: mine isn't!). +** Here's 3 ways to calculate the OUI from the ID registers. */ static int mii_get_oui(u_char phyaddr, u_long ioaddr) @@ -4380,7 +5008,7 @@ mii_get_phy(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); int id; @@ -4393,7 +5021,7 @@ if (i==0) n++; /* Count cycles */ while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ id = mii_get_oui(i, DE4X5_MII); - if ((id == 0) || (id == -1)) continue; /* Valid ID? */ + if ((id == 0) || (id == 65535)) continue; /* Valid ID? */ for (j=0; jphy[k].id && (k < DE4X5_MAX_PHY); k++); @@ -4404,11 +5032,28 @@ lp->mii_cnt++; lp->active++; } else { - i = DE4X5_MAX_MII; /* Stop the search */ - j = limit; + goto purgatory; /* Stop the search */ } + break; + } + if ((j == limit) && (i < DE4X5_MAX_MII)) { + for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); + lp->phy[k].addr = i; + lp->phy[k].id = id; + lp->phy[k].spd.reg = GENERIC_REG; /* ANLPA register */ + lp->phy[k].spd.mask = GENERIC_MASK; /* 100Mb/s technologies */ + lp->phy[k].spd.value = GENERIC_VALUE; /* TX & T4, H/F Duplex */ + lp->mii_cnt++; + lp->active++; + printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); + j = de4x5_debug; + de4x5_debug |= DEBUG_MII; + de4x5_dbg_mii(dev, k); + de4x5_debug = j; + printk("\n"); } } + purgatory: lp->active = 0; if (lp->phy[0].id) { /* Reset the PHY devices */ for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ @@ -4418,7 +5063,8 @@ de4x5_dbg_mii(dev, k); } } - + if (!lp->mii_cnt) lp->useMII = FALSE; + return lp->mii_cnt; } @@ -4476,9 +5122,11 @@ de4x5_switch_mac_port(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; s32 omr; + STOP_DE4X5; + /* Assert the OMR_PS bit in CSR6 */ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX)); @@ -4489,10 +5137,12 @@ /* Soft Reset */ RESET_DE4X5; - /* Restore the GEP */ + /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ if (lp->chipset == DC21140) { - outl(lp->cache.gepc, DE4X5_GEP); - outl(lp->cache.gep, DE4X5_GEP); + gep_wr(lp->cache.gepc, dev); + gep_wr(lp->cache.gep, dev); + } else if ((lp->chipset & ~0x0ff) == DC2114x) { + reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); } /* Restore CSR6 */ @@ -4505,6 +5155,36 @@ } static void +gep_wr(s32 data, struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + outl(data, DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); + } + + return; +} + +static int +gep_rd(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + + if (lp->chipset == DC21140) { + return inl(DE4X5_GEP); + } else if ((lp->chipset & ~0x00ff) == DC2114x) { + return (inl(DE4X5_SIGR) & 0x000fffff); + } + + return 0; +} + +static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -4530,7 +5210,7 @@ yawn(struct device *dev, int state) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; @@ -4575,6 +5255,49 @@ } static void +de4x5_parse_params(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + char *p, *q, t; + + lp->params.fdx = 0; + lp->params.autosense = AUTO; + + if (args == NULL) return; + + if ((p = strstr(args, dev->name))) { + if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); + t = *q; + *q = '\0'; + + if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + + if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { + if (strstr(p, "TP")) { + lp->params.autosense = TP; + } else if (strstr(p, "TP_NW")) { + lp->params.autosense = TP_NW; + } else if (strstr(p, "BNC")) { + lp->params.autosense = BNC; + } else if (strstr(p, "AUI")) { + lp->params.autosense = AUI; + } else if (strstr(p, "BNC_AUI")) { + lp->params.autosense = BNC; + } else if (strstr(p, "10Mb")) { + lp->params.autosense = _10Mb; + } else if (strstr(p, "100Mb")) { + lp->params.autosense = _100Mb; + } else if (strstr(p, "AUTO")) { + lp->params.autosense = AUTO; + } + } + *q = t; + } + + return; +} + +static void de4x5_dbg_open(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -4629,10 +5352,11 @@ de4x5_dbg_mii(struct device *dev, int k) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; + u_long iobase = dev->base_addr; if (de4x5_debug & DEBUG_MII) { - printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); + printk("\nMII device address: %d\n", lp->phy[k].addr); + printk("MII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); @@ -4659,25 +5383,18 @@ if (lp->media != lp->c_media) { if (de4x5_debug & DEBUG_MEDIA) { - if (lp->chipset != DC21140) { - printk("%s: media is %s\n", dev->name, - (lp->media == NC ? "unconnected!" : - (lp->media == TP ? "TP." : - (lp->media == ANS ? "TP/Nway." : - (lp->media == BNC ? "BNC." : - (lp->media == AUI ? "AUI." : - (lp->media == BNC_AUI ? "BNC/AUI." : - (lp->media == EXT_SIA ? "EXT SIA." : - "???." - )))))))); - } else { - printk("%s: mode is %s\n", dev->name, - (lp->media == NC ? "link down or incompatible connection.": - (lp->media == _100Mb ? "100Mb/s." : - (lp->media == _10Mb ? "10Mb/s." : - "\?\?\?" - )))); - } + printk("%s: media is %s%s\n", dev->name, + (lp->media == NC ? "unconnected, link down or incompatible connection" : + (lp->media == TP ? "TP" : + (lp->media == ANS ? "TP/Nway" : + (lp->media == BNC ? "BNC" : + (lp->media == AUI ? "AUI" : + (lp->media == BNC_AUI ? "BNC/AUI" : + (lp->media == EXT_SIA ? "EXT SIA" : + (lp->media == _100Mb ? "100Mb/s" : + (lp->media == _10Mb ? "10Mb/s" : + "???" + ))))))))), (lp->fdx?" full duplex.":".")); } lp->c_media = lp->media; } @@ -4749,7 +5466,8 @@ /* ** Perform IOCTL call functions here. Some are privileged operations and the -** effective uid is checked in those cases. +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. */ static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) @@ -4791,7 +5509,7 @@ } build_setup_frame(dev, PHYS_ADDR_ONLY); /* Set up the descriptor and give ownership to the card */ - while (set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/ + while (test_and_set_bit(0, (void *)&dev->tbusy) != 0); load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | SETUP_FRAME_LEN, NULL); lp->tx_new = (++lp->tx_new) % lp->txRingSize; @@ -4804,6 +5522,7 @@ omr = inl(DE4X5_OMR); omr |= OMR_PR; outl(omr, DE4X5_OMR); + dev->flags |= IFF_PROMISC; } else { status = -EPERM; } @@ -4814,6 +5533,7 @@ omr = inl(DE4X5_OMR); omr &= ~OMR_PR; outb(omr, DE4X5_OMR); + dev->flags &= ~IFF_PROMISC; } else { status = -EPERM; } @@ -4823,13 +5543,7 @@ printk("%s: Boo!\n", dev->name); break; - case DE4X5_GET_MCA: /* Get the multicast address table */ - break; - case DE4X5_SET_MCA: /* Set a multicast address */ - break; - case DE4X5_CLR_MCA: /* Clear all multicast addresses */ - break; - case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/ + case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PM; @@ -4894,9 +5608,8 @@ } break; -/* -#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * / - +#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* case DE4X5_DUMP: j = 0; tmp.addr[j++] = dev->irq; @@ -4950,7 +5663,7 @@ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4; tmp.lval[j>>2] = lp->chipset; j+=4; if (lp->chipset == DC21140) { - tmp.lval[j>>2] = inl(DE4X5_GEP); j+=4; + tmp.lval[j>>2] = gep_rd(dev); j+=4; } else { tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; @@ -5003,21 +5716,33 @@ #define LP(a) ((struct de4x5_private *)(a)) static struct device *mdev = NULL; static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(io, "i"); +#endif /* LINUX_VERSION_CODE */ int init_module(void) { + int i, num, status = -EIO; struct device *p; - if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) - return -ENOMEM; + num = count_adapters(); + + for (i=0; ipriv)->next_module) { - if (register_netdev(p) != 0) - return -EIO; + if (register_netdev(p) != 0) { + kfree(p); + } else { + status = 0; /* At least one adapter will work */ + lastModule = p; + } } - return 0; + return status; } void @@ -5051,6 +5776,62 @@ kfree(p); /* Free the device structure */ return next; +} + +static int +count_adapters(void) +{ + int i, j; + char name[DE4X5_STRLEN]; + u_char pb, dev_fn, dev_num; + u_short dev_id, vendor; + u_int class = DE4X5_CLASS_CODE; + u_int device; +#ifndef __sparc_v9__ + u_long iobase = 0x1000; + + for (j=0, i=1; iname = (char *)(new + 1); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + } + + return new; } #endif /* MODULE */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/de4x5.h linux/drivers/net/de4x5.h --- v2.0.33/linux/drivers/net/de4x5.h Tue Aug 12 13:21:12 1997 +++ linux/drivers/net/de4x5.h Wed Jun 3 15:17:47 1998 @@ -120,7 +120,7 @@ #define DC21140 DC21140_DID #define DC2114x DC2114x_DID #define DC21142 (DC2114x_DID | 0x0010) -#define DC21143 (DC2114x_DID | 0x0020) +#define DC21143 (DC2114x_DID | 0x0030) #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) @@ -166,7 +166,7 @@ /* ** PCI Configuration Base I/O Address Register (PCI_CBIO) */ -#define CBIO_MASK 0xffffff80 /* Base I/O Address Mask */ +#define CBIO_MASK -128 /* Base I/O Address Mask */ #define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ /* @@ -360,6 +360,12 @@ #define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ #define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ +#define OMR_DEF (OMR_SDP) +#define OMR_SIA (OMR_SDP | OMR_TTM) +#define OMR_SYM (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10 (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) + /* ** DC21040 Interrupt Mask Register (DE4X5_IMR) */ @@ -606,48 +612,60 @@ ** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) */ /* Valid ONLY for DE500 hardware */ -#define GEP_LNP 0x00000080 /* Link Pass (input) */ -#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ -#define GEP_SDET 0x00000020 /* Signal Detect (input) */ -#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ -#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ -#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ -#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ -#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ -#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ -#define GEP_CTRL 0x00000100 /* GEP control bit */ +#define GEP_LNP 0x00000080 /* Link Pass (input) */ +#define GEP_SLNK 0x00000040 /* SYM LINK (input) */ +#define GEP_SDET 0x00000020 /* Signal Detect (input) */ +#define GEP_HRST 0x00000010 /* Hard RESET (to PHY) (output) */ +#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */ +#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */ +#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ +#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ +#define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ +#define GEP_CTRL 0x00000100 /* GEP control bit */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f /* Autonegotiation disabled */ +#define CSR15 0x00000008 /* ** SIA Status Register (DE4X5_SISR) */ -#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ -#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ -#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ -#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ -#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ +#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ +#define SISR_LPN 0x00008000 /* Link Partner Negotiable */ +#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ #define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ -#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ -#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ -#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ -#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ -#define SISR_DAO 0x00000080 /* PLL All One */ -#define SISR_DAZ 0x00000040 /* PLL All Zero */ -#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ -#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ -#define SISR_APS 0x00000008 /* Auto Polarity State */ -#define SISR_LKF 0x00000004 /* Link Fail Status */ -#define SISR_NCR 0x00000002 /* Network Connection Error */ -#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ - -#define ANS_NDIS 0x00000000 /* Nway disable */ -#define ANS_TDIS 0x00001000 /* Transmit Disable */ -#define ANS_ADET 0x00002000 /* Ability Detect */ -#define ANS_ACK 0x00003000 /* Acknowledge */ -#define ANS_CACK 0x00004000 /* Complete Acknowledge */ -#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ -#define ANS_LCHK 0x00006000 /* Link Check */ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ +#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ +#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ +#define SISR_DAO 0x00000080 /* PLL All One */ +#define SISR_DAZ 0x00000040 /* PLL All Zero */ +#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */ +#define SISR_DSD 0x00000010 /* PLL Self-Test Done */ +#define SISR_APS 0x00000008 /* Auto Polarity State */ +#define SISR_LKF 0x00000004 /* Link Fail Status */ +#define SISR_LS10 0x00000004 /* 10Mb/s Link Fail Status */ +#define SISR_NCR 0x00000002 /* Network Connection Error */ +#define SISR_LS100 0x00000002 /* 100Mb/s Link Fail Status */ +#define SISR_PAUI 0x00000001 /* AUI_TP Indication */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ + +#define ANS_NDIS 0x00000000 /* Nway disable */ +#define ANS_TDIS 0x00001000 /* Transmit Disable */ +#define ANS_ADET 0x00002000 /* Ability Detect */ +#define ANS_ACK 0x00003000 /* Acknowledge */ +#define ANS_CACK 0x00004000 /* Complete Acknowledge */ +#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */ +#define ANS_LCHK 0x00006000 /* Link Check */ + +#define SISR_RST 0x00000301 /* CSR12 reset */ +#define SISR_ANR 0x00001301 /* Autonegotiation restart */ /* ** SIA Connectivity Register (DE4X5_SICR) @@ -791,16 +809,22 @@ /* ** Media / mode state machine definitions +** User selectable: */ -#define NC 0x0000 /* No Connection */ #define TP 0x0001 /* 10Base-T */ #define TP_NW 0x0002 /* 10Base-T with Nway */ #define BNC 0x0004 /* Thinwire */ #define AUI 0x0008 /* Thickwire */ #define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ -#define ANS 0x0020 /* Intermediate AutoNegotiation State */ #define _10Mb 0x0040 /* 10Mb/s Ethernet */ #define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define AUTO 0x4000 /* Auto sense the media or speed */ + +/* +** Internal states +*/ +#define NC 0x0000 /* No Connection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ #define SPD_DET 0x0100 /* Parallel speed detection */ #define INIT 0x0200 /* Initial state */ #define EXT_SIA 0x0400 /* External SIA for motherboard chip */ @@ -812,7 +836,6 @@ #define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ #define MII 0x1000 /* MII on the 21143 */ -#define AUTO 0x4000 /* Auto sense the media or speed */ #define TIMER_CB 0x80000000 /* Timer callback detection */ /* @@ -828,6 +851,7 @@ #define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ #define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ #define DEBUG_PCICFG 0x0100 +#define DEBUG_ALL 0x01ff /* ** Miscellaneous @@ -884,6 +908,12 @@ #define OPEN 2 /* Running */ /* +** Various wait times +*/ +#define PDET_LINK_WAIT 1200 /* msecs to wait for link detect bits */ +#define ANS_FINISH_WAIT 1000 /* msecs to wait for link detect bits */ + +/* ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since ** the vendors seem split 50-50 on how to calculate the OUI register values ** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. @@ -908,12 +938,13 @@ } else if (lp->useSROM && !lp->useMII) {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ + outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_TTM, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ + gep_wr(lp->cache.gep, dev);\ }\ } @@ -936,12 +967,13 @@ } else if (lp->useSROM && !lp->useMII) {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ omr |= (lp->fdx ? OMR_FDX : 0);\ - outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ + gep_wr(lp->cache.gep, dev);\ }\ } @@ -956,8 +988,9 @@ outl(omr, DE4X5_OMR);\ } else {\ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ - outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ + outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ lp->cache.gep = (GEP_FDXD | GEP_MODE);\ + gep_wr(lp->cache.gep, dev);\ }\ } @@ -991,3 +1024,5 @@ #define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */ #define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */ #define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) diff -u --recursive --new-file v2.0.33/linux/drivers/net/dlci.c linux/drivers/net/dlci.c --- v2.0.33/linux/drivers/net/dlci.c Sun Sep 15 00:34:18 1996 +++ linux/drivers/net/dlci.c Wed Jun 3 15:17:47 1998 @@ -27,6 +27,7 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/net/eepro.c linux/drivers/net/eepro.c --- v2.0.33/linux/drivers/net/eepro.c Sat Aug 10 00:03:14 1996 +++ linux/drivers/net/eepro.c Wed Jun 3 15:17:47 1998 @@ -1,6 +1,7 @@ /* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */ /* Written 1994, 1995,1996 by Bao C. Ha. + Last Update, 2/7/98, Phillip R. Jaenke Copyright (C) 1994, 1995,1996 by Bao C. Ha. @@ -11,6 +12,9 @@ The author may be reached at bao.ha@srs.gov or 418 Hastings Place, Martinez, GA 30907. + Phillip R. Jaenke may be reached at prj@nls.net + or P.O. Box 413, Twinsburg, OH 44087, ATTN: Linux + Things remaining to do: Better record keeping of errors. Eliminate transmit interrupt to reduce overhead. @@ -23,6 +27,9 @@ This is a compatibility hardware problem. Versions: + + 0.10 Added several I/O addresses previously not included. + (PRJ, 2/7/98) 0.09 Fixed a race condition in the transmit algorithm, which causes crashes under heavy load with fast @@ -60,7 +67,7 @@ */ static const char *version = - "eepro.c: v0.09 7/31/96 Bao C. Ha (bao.ha@srs.gov)\n"; + "eepro.c: v0.10 7/31/96 Bao C. Ha (bao.ha@srs.gov)\n"; #include @@ -110,7 +117,7 @@ /* First, a few definitions that the brave might change. */ /* A zero-terminated list of I/O addresses to be probed. */ static unsigned int eepro_portlist[] = - { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360, 0}; + { 0x200, 0x240, 0x260, 0x280, 0x2C0, 0x300, 0x310, 0x320, 0x340, 0x360, 0}; /* use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG diff -u --recursive --new-file v2.0.33/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c --- v2.0.33/linux/drivers/net/eepro100.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/net/eepro100.c Wed Jun 3 15:17:47 1998 @@ -1,7 +1,7 @@ /* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */ /* NOTICE: this version tested with kernels 1.3.72 and later only! - Written 1996-1997 by Donald Becker. + Written 1996-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -19,7 +19,7 @@ */ static const char *version = -"eepro100.c:v0.36 10/20/97 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n"; +"eepro100.c:v0.99B 4/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values that apply to all boards. First set are undocumented and spelled per Intel recommendations. */ @@ -38,6 +38,9 @@ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */ +static int multicast_filter_limit = 64; + #include #ifdef MODULE #ifdef MODVERSIONS @@ -69,73 +72,36 @@ #include #include #include - -/* A nominally proper method to handle version dependencies is to use - LINUX_VERSION_CODE in version.h, but that triggers recompiles w/'make'. */ -#define VERSION(v,p,s) (((v)<<16)+(p<<8)+s) -#ifdef MODULE -#if (LINUX_VERSION_CODE < VERSION(1,3,0)) -#define KERNEL_1_2 -#else /* 1.3.0 */ -#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) -#define NEW_MULTICAST -#define LINUX_1_4 -#else -#warning "This driver is tested for 1.3.44 and later development kernels only." -#endif /* 1.3.44 */ -#endif -#else - -#if (LINUX_VERSION_CODE >= 0x10344) -#define NEW_MULTICAST #include -#endif -#ifdef HAVE_HEADER_CACHE -#define LINUX_1_4 -#define NEW_MULTICAST -#else -#ifdef ETH_P_DDCMP /* Warning: Bogus! This means IS_LINUX_1_3. */ -#define KERNEL_1_3 -#else -#define KERNEL_1_2 -#endif +/* Unused in the 2.0.* version, but retained for documentation. */ +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(congenb, "i"); +MODULE_PARM(txfifo, "i"); +MODULE_PARM(rxfifo, "i"); +MODULE_PARM(txdmacount, "i"); +MODULE_PARM(rxdmacount, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(multicast_filter_limit, "i"); #endif -#endif -/* This should be in a header file. */ -#if (LINUX_VERSION_CODE < VERSION(1,3,44)) -struct device *init_etherdev(struct device *dev, int sizeof_priv, - unsigned long *mem_startp); -#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) -#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) -#endif #if (LINUX_VERSION_CODE < 0x20123) #define test_and_set_bit(val, addr) set_bit(val, addr) #endif -/* The total I/O port extent of the board. Nominally 0x18, but rounded up - for PCI allocation. */ +/* The total I/O port extent of the board. + The registers beyond 0x18 only exist on the i82558. */ #define SPEEDO3_TOTAL_SIZE 0x20 -#ifdef HAVE_DEVLIST -struct netdev_entry eepro100_drv = -{"EEPro-100", eepro100_init, SPEEDO3_TOTAL_SIZE, NULL}; -#endif - -#ifdef SPEEDO3_DEBUG -int speedo_debug = SPEEDO3_DEBUG; -#else -int speedo_debug = 3; -#endif +int speedo_debug = 1; /* Theory of Operation @@ -274,7 +240,7 @@ #define PKT_BUF_SZ 1536 /* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((400*HZ)/1000) +#define TX_TIMEOUT ((800*HZ)/1000) /* How to wait for the command unit to accept a command. Typically this takes 0 ticks. */ @@ -287,13 +253,6 @@ /* Operational parameter that usually are not changed. */ -#ifndef PCI_VENDOR_ID_INTEL /* Now defined in linux/pci.h */ -#define PCI_VENDOR_ID_INTEL 0x8086 /* Hmmmm, how did they pick that? */ -#endif -#ifndef PCI_DEVICE_ID_INTEL_82557 -#define PCI_DEVICE_ID_INTEL_82557 0x1229 -#endif - /* The rest of these values should never change. */ /* Offsets to the various registers. @@ -393,9 +352,6 @@ /* Rx descriptor ring & addresses of receive-in-place skbuffs. */ struct RxFD *rx_ringp[RX_RING_SIZE]; struct sk_buff* rx_skbuff[RX_RING_SIZE]; -#if (LINUX_VERSION_CODE < 0x10300) /* Kernel v1.2.*. */ - struct RxFD saved_skhead[RX_RING_SIZE]; /* Saved skbuff header chunk. */ -#endif struct RxFD *last_rxf; /* Last command sent. */ struct enet_statistics stats; struct speedo_stats lstats; @@ -407,6 +363,7 @@ 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 in_interrupt; /* Word-aligned dev->interrupt */ char rx_mode; /* Current PROMISC/ALLMULTI setting. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ @@ -437,7 +394,7 @@ static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; static void speedo_found1(struct device *dev, int ioaddr, int irq, - int options, int card_idx); + int card_idx); static int read_eeprom(int ioaddr, int location); static int mdio_read(int ioaddr, int phy_id, int location); @@ -447,16 +404,10 @@ static void speedo_init_rx_ring(struct device *dev); static int speedo_start_xmit(struct sk_buff *skb, struct device *dev); static int speedo_rx(struct device *dev); -#ifdef SA_SHIRQ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -#else -static void speedo_interrupt(int irq, struct pt_regs *regs); -#endif static int speedo_close(struct device *dev); static struct enet_statistics *speedo_get_stats(struct device *dev); -#ifdef HAVE_PRIVATE_IOCTL static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd); -#endif static void set_rx_mode(struct device *dev); @@ -465,8 +416,8 @@ /* 'options' is used to pass a transceiver override or full-duplex flag e.g. "options=16" for FD, "options=32" for 100mbps-only. */ static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -#ifdef MODULE static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +#ifdef MODULE static int debug = -1; /* The debug level */ #endif @@ -481,12 +432,9 @@ static int pci_index = 0; for (; pci_index < 8; pci_index++) { unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency; -#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) int pci_ioaddr; -#else - long pci_ioaddr; -#endif - unsigned short pci_command; + + unsigned short pci_command, new_command; if (pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557, @@ -507,29 +455,25 @@ /* Get and check the bus-master and latency values. */ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); - if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk(" PCI Master Bit has not been set! Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; + 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, pci_command); + PCI_COMMAND, new_command); } pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 10) { + if (pci_latency < 32) { printk(" PCI latency timer (CFLT) is unreasonably low at %d." - " Setting to 255 clocks.\n", pci_latency); + " Setting to 32 clocks.\n", pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 255); + PCI_LATENCY_TIMER, 32); } else if (speedo_debug > 1) printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); -#ifdef MODULE - speedo_found1(dev, pci_ioaddr, pci_irq_line, options[cards_found], - cards_found); -#else - speedo_found1(dev, pci_ioaddr, pci_irq_line, - dev ? dev->mem_start : 0, -1); -#endif + speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found); dev = NULL; cards_found++; } @@ -538,23 +482,26 @@ return cards_found; } -static void speedo_found1(struct device *dev, int ioaddr, int irq, int options, +static void speedo_found1(struct device *dev, int ioaddr, int irq, int card_idx) { static int did_version = 0; /* Already printed version info. */ struct speedo_private *sp; char *product; - int i; + int i, option; u16 eeprom[0x40]; if (speedo_debug > 0 && did_version++ == 0) printk(version); -#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) dev = init_etherdev(dev, sizeof(struct speedo_private)); -#else - dev = init_etherdev(dev, sizeof(struct speedo_private), 0); -#endif + + if (dev->mem_start > 0) + option = dev->mem_start; + else if (card_idx >= 0 && options[card_idx] >= 0) + option = options[card_idx]; + else + option = 0; /* Read the station address EEPROM before doing the reset. Perhaps this should even be done before accepting the device, @@ -618,17 +565,6 @@ if (eeprom[7] & 0x0700) printk(KERN_INFO " Secondary interface chip %s.\n", phys[(eeprom[7]>>8)&7]); -#if defined(notdef) - /* ToDo: Read and set PHY registers through MDIO port. */ - for (i = 0; i < 2; i++) - printk(KERN_INFO" MDIO register %d is %4.4x.\n", - i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); - for (i = 5; i < 7; i++) - printk(KERN_INFO" MDIO register %d is %4.4x.\n", - i, mdio_read(ioaddr, eeprom[6] & 0x1f, i)); - printk(KERN_INFO" MDIO register %d is %4.4x.\n", - 25, mdio_read(ioaddr, eeprom[6] & 0x1f, 25)); -#endif if (((eeprom[6]>>8) & 0x3f) == DP83840 || ((eeprom[6]>>8) & 0x3f) == DP83840A) { int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422; @@ -638,13 +574,13 @@ mdi_reg23); mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23); } - if ((options >= 0) && (options & 0x60)) { + if ((option >= 0) && (option & 0x70)) { printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n", - (options & 0x20 ? 100 : 10), - (options & 0x10 ? "full" : "half")); + (option & 0x20 ? 100 : 10), + (option & 0x10 ? "full" : "half")); mdio_write(ioaddr, eeprom[6] & 0x1f, 0, - ((options & 0x20) ? 0x2000 : 0) | /* 100mbps? */ - ((options & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + ((option & 0x20) ? 0x2000 : 0) | /* 100mbps? */ + ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ } /* Perform a system self-test. */ @@ -653,11 +589,7 @@ self_test_results[1] = -1; outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort); do { -#ifdef _LINUX_DELAY_H udelay(10); -#else - SLOW_DOWN_IO; -#endif } while (self_test_results[1] == -1 && --boguscnt >= 0); if (boguscnt < 0) { /* Test optimized out. */ @@ -694,12 +626,12 @@ sp->next_module = root_speedo_dev; root_speedo_dev = dev; + sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0; if (card_idx >= 0) { if (full_duplex[card_idx] >= 0) sp->full_duplex = full_duplex[card_idx]; - } else - sp->full_duplex = options >= 0 && (options & 0x10) ? 1 : 0; - sp->default_port = options >= 0 ? (options & 0x0f) : 0; + } + sp->default_port = option >= 0 ? (option & 0x0f) : 0; sp->phy[0] = eeprom[6]; sp->phy[1] = eeprom[7]; @@ -713,12 +645,8 @@ dev->hard_start_xmit = &speedo_start_xmit; dev->stop = &speedo_close; dev->get_stats = &speedo_get_stats; -#ifdef NEW_MULTICAST dev->set_multicast_list = &set_rx_mode; -#endif -#ifdef HAVE_PRIVATE_IOCTL dev->do_ioctl = &speedo_ioctl; -#endif return; } @@ -735,13 +663,8 @@ #define EE_ENB (0x4800 | EE_CS) /* Delay between EEPROM clock transitions. - This is a "nasty" timing loop, but PC compatible machines are defined - to delay an ISA compatible period for the SLOW_DOWN_IO macro. */ -#ifdef _LINUX_DELAY_H + This will actually work with no delay on 33Mhz PCI. */ #define eeprom_delay(nanosec) udelay(1); -#else -#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) -#endif /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5 << 6) @@ -765,8 +688,6 @@ eeprom_delay(100); outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(150); - outw(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); } outw(EE_ENB, ee_addr); @@ -785,14 +706,9 @@ static int mdio_read(int ioaddr, int phy_id, int location) { - int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */ outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); do { -#ifdef _LINUX_DELAY_H - udelay(16); -#else - SLOW_DOWN_IO; -#endif val = inl(ioaddr + SCBCtrlMDI); if (--boguscnt < 0) { printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val); @@ -803,15 +719,10 @@ static int mdio_write(int ioaddr, int phy_id, int location, int value) { - int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */ outl(0x04000000 | (location<<16) | (phy_id<<21) | value, ioaddr + SCBCtrlMDI); do { -#ifdef _LINUX_DELAY_H - udelay(16); -#else - SLOW_DOWN_IO; -#endif val = inl(ioaddr + SCBCtrlMDI); if (--boguscnt < 0) { printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val); @@ -830,31 +741,13 @@ #ifdef notdef /* We could reset the chip, but should not need to. */ outl(0, ioaddr + SCBPort); - for (i = 40; i >= 0; i--) - SLOW_DOWN_IO; /* At least 250ns */ + udelay(10); #endif -#ifdef SA_SHIRQ if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, "Intel EtherExpress Pro 10/100 Ethernet", dev)) { return -EAGAIN; } -#else -#ifdef USE_SHARED_IRQ - if (request_shared_irq(dev->irq, &speedo_interrupt, dev, - "Intel EtherExpress Pro 10/100 Ethernet")) - return -EAGAIN; -#else - if (dev->irq < 2 || dev->irq > 15 || irq2dev_map[dev->irq] != NULL) - return -EAGAIN; - irq2dev_map[dev->irq] = dev; - if (request_irq(dev->irq, &speedo_interrupt, 0, "Intel EtherExpress Pro 10/100 Ethernet")) { - irq2dev_map[dev->irq] = NULL; - return -EAGAIN; - } -#endif -#endif - if (speedo_debug > 1) printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq); @@ -899,6 +792,7 @@ dev->if_port = sp->default_port; + sp->in_interrupt = 0; dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; @@ -973,36 +867,23 @@ for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; -#ifndef KERNEL_1_2 - skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD)); -#else skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC); -#endif sp->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 rxf = (struct RxFD *)skb->tail; skb_reserve(skb, sizeof(struct RxFD)); -#else - /* Save the data in the header region -- it's restored later. */ - rxf = (struct RxFD *)(skb->data - sizeof(struct RxFD)); - memcpy(&sp->saved_skhead[i], rxf, sizeof(struct RxFD)); -#endif sp->rx_ringp[i] = rxf; 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. */ -#if LINUX_VERSION_CODE < 0x10300 /* This field unused by i82557, we use it as a consistency check. */ - rxf->rx_buf_addr = virt_to_bus(skb->data); -#else rxf->rx_buf_addr = virt_to_bus(skb->tail); -#endif + rxf->count = 0; rxf->size = PKT_BUF_SZ; } @@ -1015,27 +896,11 @@ { struct speedo_private *sp = (struct speedo_private *)dev->priv; int ioaddr = dev->base_addr; - int i; printk(KERN_WARNING "%s: Transmit timed out: status %4.4x " "command %4.4x.\n", dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd)); -#ifndef final_version - printk(KERN_WARNING "%s: Tx timeout fill index %d scavenge index %d.\n", - dev->name, sp->cur_tx, sp->dirty_tx); - printk(KERN_WARNING " Tx queue "); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (int)sp->tx_ring[i].status); - printk(".\n" KERN_WARNING " Rx ring "); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (int)sp->rx_ringp[i]->status); - printk(".\n"); -#else - dev->if_port ^= 1; - printk(KERN_WARNING " (Media type switching not yet implemented.)\n"); - /* Do not do 'dev->tbusy = 0;' there -- it is incorrect. */ -#endif if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) { printk(KERN_WARNING "%s: Trying to restart the transmitter...\n", dev->name); @@ -1045,9 +910,14 @@ } else { outw(DRVR_INT, ioaddr + SCBCmd); } - /* Reset the MII transceiver. */ - if ((sp->phy[0] & 0x8000) == 0) - mdio_write(ioaddr, sp->phy[0] & 0x1f, 0, 0x8000); + /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */ + if ((sp->phy[0] & 0x8000) == 0) { + int phy_addr = sp->phy[0] & 0x1f; + mdio_write(ioaddr, phy_addr, 0, 0x0400); + mdio_write(ioaddr, phy_addr, 1, 0x0000); + mdio_write(ioaddr, phy_addr, 4, 0x0000); + mdio_write(ioaddr, phy_addr, 0, 0x8000); + } sp->stats.tx_errors++; dev->trans_start = jiffies; return; @@ -1060,13 +930,6 @@ int ioaddr = dev->base_addr; int entry; - if (skb == NULL || skb->len <= 0) { - printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - 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. If this ever occurs the queue layer is doing something evil! */ @@ -1080,7 +943,7 @@ return 1; } speedo_tx_timeout(dev); - return 0; + return 1; } /* Caution: the write order is important here, set the base address @@ -1123,7 +986,7 @@ if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3) sp->tx_full = 1; else - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); dev->trans_start = jiffies; @@ -1132,21 +995,9 @@ /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -#ifdef SA_SHIRQ static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) -#else -static void speedo_interrupt(int irq, struct pt_regs *regs) -#endif { -#ifdef SA_SHIRQ struct device *dev = (struct device *)dev_instance; -#else -#ifdef USE_SHARED_IRQ - struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]); -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif -#endif struct speedo_private *sp; int ioaddr, boguscnt = max_interrupt_work; unsigned short status; @@ -1161,8 +1012,10 @@ ioaddr = dev->base_addr; sp = (struct speedo_private *)dev->priv; #ifndef final_version - if (dev->interrupt) { - printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + /* A lock to prevent simultaneous entry on SMP machines. */ + if (test_and_set_bit(0, (void*)&sp->in_interrupt)) { + printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", + dev->name); return; } dev->interrupt = 1; @@ -1184,19 +1037,6 @@ speedo_rx(dev); if (status & 0x1000) { -#ifdef notdef - int i; - printk(KERN_WARNING"%s: The EEPro100 receiver left the ready" - " state -- %4.4x! Index %d (%d).\n", dev->name, status, - sp->cur_rx, sp->cur_rx % RX_RING_SIZE); - printk(KERN_WARNING " Rx ring:\n "); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %d %8.8x %8.8x %8.8x %d %d.\n", - i, sp->rx_ringp[i]->status, sp->rx_ringp[i]->link, - sp->rx_ringp[i]->rx_buf_addr, sp->rx_ringp[i]->count, - sp->rx_ringp[i]->size); -#endif - if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */ outw(RX_RESUMENR, ioaddr + SCBCmd); else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */ @@ -1243,7 +1083,7 @@ && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) { /* The ring is no longer full, clear tbusy. */ sp->tx_full = 0; - dev->tbusy = 0; + clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } @@ -1263,23 +1103,8 @@ printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", dev->name, inw(ioaddr + SCBStatus)); -#ifndef final_version - /* Special code for testing *only*. */ - { - static int stopit = 100; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ALERT "%s: Emergency stop, interrupt is stuck.\n", - dev->name); -#ifdef SA_SHIRQ - free_irq(irq, dev); -#else - free_irq(irq); -#endif - } - } -#endif - dev->interrupt = 0; + clear_bit(0, (void*)&sp->in_interrupt); return; } @@ -1320,16 +1145,6 @@ /* Pass up the skb already on the Rx ring. */ skb = sp->rx_skbuff[entry]; -#ifdef KERNEL_1_2 - temp = skb->data; - 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: %p vs. %p / %p.\n", dev->name, - bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), - temp, skb->data); - /* Get a fresh skbuff to replace the filled one. */ - newskb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC); -#else 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" @@ -1337,36 +1152,21 @@ 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)); -#endif + if (newskb) { struct RxFD *rxf; rx_in_place = 1; sp->rx_skbuff[entry] = newskb; newskb->dev = dev; -#ifdef KERNEL_1_2 - /* Restore the data in the old header region. */ - memcpy(skb->data - sizeof(struct RxFD), - &sp->saved_skhead[entry], sizeof(struct RxFD)); - /* Save the data in this header region. */ - rxf = (struct RxFD *)(newskb->data - sizeof(struct RxFD)); - sp->rx_ringp[entry] = rxf; - memcpy(&sp->saved_skhead[entry], rxf, sizeof(struct RxFD)); - rxf->rx_buf_addr = virt_to_bus(newskb->data); -#else 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); -#endif rxf->status = 0x00000001; } else /* No memory, drop the packet. */ skb = 0; } else -#ifdef KERNEL_1_2 - skb = alloc_skb(pkt_len, GFP_ATOMIC); -#else skb = dev_alloc_skb(pkt_len + 2); -#endif if (skb == NULL) { int i; printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name); @@ -1388,7 +1188,6 @@ break; } skb->dev = dev; -#if (LINUX_VERSION_CODE >= VERSION(1,3,44)) if (! rx_in_place) { skb_reserve(skb, 2); /* 16 byte align the data fields */ #if defined(__i386) && notyet @@ -1401,22 +1200,6 @@ #endif } skb->protocol = eth_type_trans(skb, dev); -#else -#ifdef KERNEL_1_3 -#warning This code has only been tested with later 1.3.* kernels. - skb->len = pkt_len; - memcpy(skb->data, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), - pkt_len); - /* Needed for 1.3.*. */ - skb->protocol = eth_type_trans(skb, dev); -#else /* KERNEL_1_2 */ - skb->len = pkt_len; - if (! rx_in_place) { - memcpy(skb->data, - bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len); - } -#endif -#endif netif_rx(skb); sp->stats.rx_packets++; } @@ -1460,12 +1243,7 @@ outw(INT_MASK, ioaddr + SCBCmd); outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif /* Free all the skbuffs in the Rx and Tx queues. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -1549,7 +1327,6 @@ return &sp->stats; } -#ifdef HAVE_PRIVATE_IOCTL static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd) { struct speedo_private *sp = (struct speedo_private *)dev->priv; @@ -1572,7 +1349,6 @@ return -EOPNOTSUPP; } } -#endif /* HAVE_PRIVATE_IOCTL */ /* Set or clear the multicast filter for this adaptor. This is very ugly with Intel chips -- we usually have to execute an @@ -1594,7 +1370,8 @@ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ new_rx_mode = 3; - } else if (dev->flags & IFF_ALLMULTI) { + } else if ((dev->flags & IFF_ALLMULTI) || + dev->mc_count > multicast_filter_limit) { new_rx_mode = 1; } else new_rx_mode = 0; @@ -1681,7 +1458,7 @@ struct dev_mc_list *mclist; u16 *eaddrs; struct descriptor *mc_setup_frm = sp->mc_setup_frm; - u16 *setup_params = (u16 *)mc_setup_frm->params; + u16 *setup_params; int i; if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 @@ -1751,24 +1528,6 @@ } #ifdef MODULE -#if (LINUX_VERSION_CODE < VERSION(1,3,38)) /* 1.3.38 and later */ -char kernel_version[] = UTS_RELEASE; -#endif - -#if LINUX_VERSION_CODE > 0x20118 -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver"); -MODULE_PARM(debug, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(congenb, "i"); -MODULE_PARM(txfifo, "i"); -MODULE_PARM(rxfifo, "i"); -MODULE_PARM(txdmacount, "i"); -MODULE_PARM(rxdmacount, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(max_interrupt_work, "i"); -#endif int init_module(void) @@ -1815,7 +1574,8 @@ /* * Local variables: - * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --recursive --new-file v2.0.33/linux/drivers/net/epic100.c linux/drivers/net/epic100.c --- v2.0.33/linux/drivers/net/epic100.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/epic100.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,1179 @@ +/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Linux. */ +/* + Written 1997-1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + All other rights reserved. + + This driver is for the SMC83c170/175 "EPIC" series, as used on the + SMC EtherPower II 9432 PCI adapter, and several CardBus cards. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html +*/ + +static const char *version = +"epic100.c:v0.99B 4/7/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/epic100.html\n"; + +/* A few user-configurable values. */ + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 200; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 10; + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((2000*HZ)/1000) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* Bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */ +#define RX_FIFO_THRESH 1 /* 0-3, 0==32, 64,96, or 3==128 bytes */ + +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* Processor type for cache alignment. */ +#include +#include +#include + +#include +#include +#include + +#define RUN_AT(x) (jiffies + (x)) + +#if LINUX_VERSION_CODE < 0x20138 +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif +#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115) +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("SMC 83c170 EPIC series 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(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +#endif + +/* The I/O extent. */ +#define EPIC_TOTAL_SIZE 0x100 + +static int epic_debug = 1; + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the SMC "EPCI/100", the SMC +single-chip ethernet controllers for PCI. This chip is used on +the SMC EtherPower II boards. + + +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 will assign the +PCI INTA signal to a (preferably otherwise unused) system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Ring buffers + +IVb. References + +http://www.smc.com/components/catalog/smc83c170.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://www.national.com/pf/DP/DP83840.html + +IVc. Errata + +*/ + +#ifndef PCI_VENDOR_ID_SMC +#define PCI_VENDOR_ID_SMC 0x10B8 +#endif +#ifndef PCI_DEVICE_ID_SMC_EPIC100 +#define PCI_DEVICE_ID_SMC_EPIC100 0x0005 +#endif + +/* The rest of these values should never change. */ +/* Offsets to registers, using the (ugh) SMC names. */ +enum epic_registers { + COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14, + TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28, /* Rx error counters. */ + MIICtrl=0x30, MIIData=0x34, MIICfg=0x38, + LAN0=64, /* MAC address. */ + MC0=80, /* Multicast filter table. */ + RxCtrl=96, TxCtrl=112, TxSTAT=0x74, + PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatus { + TxIdle=0x40000, RxIdle=0x20000, + CntFull=0x0200, TxUnderrun=0x0100, + TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010, + RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001, +}; + +/* The EPIC100 Rx and Tx buffer descriptors. */ + +struct epic_tx_desc { + s16 status; + u16 txlength; + u32 bufaddr; + u16 buflength; + u16 control; + u32 next; +}; + +struct epic_rx_desc { + s16 status; + u16 rxlength; + u32 bufaddr; + u32 buflength; + u32 next; +}; + +struct epic_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + + /* Rx and Rx rings here so that they remain paragraph aligned. */ + struct epic_rx_desc rx_ring[RX_RING_SIZE]; + struct epic_tx_desc tx_ring[TX_RING_SIZE]; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + + /* Ring pointers. */ + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + + u8 pci_bus, pci_dev_fn; /* PCI bus location. */ + u16 chip_id; + + struct enet_statistics stats; + struct timer_list timer; /* Media selection timer. */ + unsigned char mc_filter[8]; + signed char phys[4]; /* MII device addresses. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex: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. */ + unsigned int mediasense:1; /* Media sensing in progress. */ + int pad0, pad1; /* Used for 8-byte alignment */ +}; + +/* Used to pass the full-duplex flag, etc. */ +#define MAX_UNITS 8 +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +static struct device *epic100_probe1(int pci_bus, int pci_devfn, + struct device *dev, int card_idx); +static int epic_open(struct device *dev); +static int read_eeprom(int ioaddr, int location); +static int mii_read(int ioaddr, int phy_id, int location); +static void epic_timer(unsigned long data); +static void epic_tx_timeout(struct device *dev); +static void epic_init_ring(struct device *dev); +static int epic_start_xmit(struct sk_buff *skb, struct device *dev); +static int epic_rx(struct device *dev); +static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int epic_close(struct device *dev); +static struct enet_statistics *epic_get_stats(struct device *dev); +static void set_rx_mode(struct device *dev); + + +/* A list of all installed EPIC devices, for removing the driver module. */ +static struct device *root_epic_dev = NULL; + +int epic100_probe(struct device *dev) +{ + static int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ + + /* 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 + Epic cards in slot order. */ + + if (pcibios_present()) { + unsigned char pci_bus, pci_device_fn; + + for (;pci_index < 0xff; pci_index++) { + u8 pci_latency; + u16 pci_command, new_command, vendor, device; + u32 pci_ioaddr; + + 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) + 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); + if (vendor != PCI_VENDOR_ID_SMC) + continue; + if (device != PCI_DEVICE_ID_SMC_EPIC100) { + printk("Unknown SMC PCI ethernet chip type %4.4x detected:" + " not configured.\n", device); + 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_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled Ethernet" + " device %4.4x-%4.4x." + " Updating PCI command %4.4x->%4.4x.\n", + vendor, device, pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } + + 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 (check_region(pci_ioaddr, EPIC_TOTAL_SIZE)) + continue; + + dev = epic100_probe1(pci_bus, pci_device_fn, dev, cards_found); + + if (dev) { + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 32) { + printk(" PCI latency timer (CFLT) value of %d is " + "unreasonably low, setting to 32.\n", pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, 32); + } else if (epic_debug > 1) + printk(" PCI latency timer (CFLT) is %#x.\n", + pci_latency); + dev = 0; + cards_found++; + } + } + } + + return cards_found ? 0 : -ENODEV; +} + +static struct device *epic100_probe1(int bus, int devfn, struct device *dev, + int card_idx) +{ + static int did_version = 0; /* Already printed version info. */ + struct epic_private *tp; + int i, option = 0, duplex = 0; + u8 irq; + u16 chip_id; + u32 ioaddr; + + if (epic_debug > 0 && did_version++ == 0) + printk(version); + + if (dev && dev->mem_start) { + option = dev->mem_start; + duplex = (dev->mem_start & 16) ? 1 : 0; + } else if (card_idx >= 0 && card_idx < MAX_UNITS) { + if (options[card_idx] >= 0) + option = options[card_idx]; + if (full_duplex[card_idx] >= 0) + duplex = full_duplex[card_idx]; + } + + dev = init_etherdev(dev, 0); + + pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &ioaddr); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &chip_id); + ioaddr &= ~3; + + printk("%s: SMC EPIC/100 at %#3x, IRQ %d, ", dev->name, ioaddr, irq); + + /* Bring the chip out of low-power mode. */ + outl(0x0200, ioaddr + GENCTL); + /* Magic?! If we don't set this bit the MII interface won't work. */ + outl(0x0008, ioaddr + TEST1); + + /* This could also be read from the EEPROM. */ + for (i = 0; i < 3; i++) + ((u16 *)dev->dev_addr)[i] = inw(ioaddr + LAN0 + i*4); + + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x.\n", dev->dev_addr[i]); + + if (epic_debug > 1) { + printk("%s: EEPROM contents\n", dev->name); + for (i = 0; i < 64; i++) + printk(" %4.4x%s", read_eeprom(ioaddr, i), i % 16 == 15 ? "\n" : ""); + } + + /* We do a request_region() to register /proc/ioports info. */ + request_region(ioaddr, EPIC_TOTAL_SIZE, "SMC EPIC/100"); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* The data structures must be quadword aligned. */ + tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); + memset(tp, 0, sizeof(*tp)); + dev->priv = tp; + + tp->next_module = root_epic_dev; + root_epic_dev = dev; + + tp->chip_id = chip_id; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ + { + int phy, phy_idx; + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + phy++) { + int mii_status = mii_read(ioaddr, phy, 0); + if (mii_status != 0xffff && mii_status != 0x0000) { + tp->phys[phy_idx++] = phy; + printk("%s: MII transceiver found at address %d.\n", + dev->name, phy); + } + } + if (phy_idx == 0) { + printk("%s: ***WARNING***: No MII transceiver found!\n", + dev->name); + /* Use the known PHY address of the EPII. */ + tp->phys[0] = 3; + } + } + + /* Leave the chip in low-power mode. */ + outl(0x0008, ioaddr + GENCTL); + + /* The lower four bits are the media type. */ + tp->full_duplex = duplex; + tp->default_port = option; + if (tp->default_port) + tp->medialock = 1; + + /* The Epic-specific entries in the device structure. */ + dev->open = &epic_open; + dev->hard_start_xmit = &epic_start_xmit; + dev->stop = &epic_close; + dev->get_stats = &epic_get_stats; + dev->set_multicast_list = &set_rx_mode; + + return dev; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x09 +#define EE_DATA_READ 0x10 /* EEPROM chip data out. */ +#define EE_ENB (0x0001 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz is untested. + */ + +#ifdef _LINUX_DELAY_H +#define eeprom_delay(nanosec) udelay(1) +#else +#define eeprom_delay(nanosec) do { ; } while (0) +#endif + +/* 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) +{ + int i; + int retval = 0; + int ee_addr = ioaddr + EECTL; + 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); + 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); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +#define MII_READOP 1 +#define MII_WRITEOP 2 +static int mii_read(int ioaddr, int phy_id, int location) +{ + int i; + + outl((phy_id << 9) | (location << 4) | MII_READOP, ioaddr + MIICtrl); + /* Typical operation takes < 50 ticks. */ + for (i = 4000; i > 0; i--) + if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) + break; + return inw(ioaddr + MIIData); +} + + +static int +epic_open(struct device *dev) +{ + struct epic_private *tp = (struct epic_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + int mii_reg5; + int full_duplex = 0; + + /* Soft reset the chip. */ + outl(0x0001, ioaddr + GENCTL); + + if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, "SMC EPIC/100", dev)) + return -EAGAIN; + + MOD_INC_USE_COUNT; + + epic_init_ring(dev); + + /* This next line by Ken Yamaguchi.. ?? */ + outl(0x8, ioaddr + 0x1c); + + /* Pull the chip out of low-power mode, enable interrupts, and set for + PCI read multiple. */ + outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL); + + for (i = 0; i < 3; i++) + outl(((u16*)dev->dev_addr)[i], ioaddr + LAN0 + i*4); + + outl(TX_FIFO_THRESH, ioaddr + TxThresh); + full_duplex = tp->full_duplex; + + mii_reg5 = mii_read(ioaddr, tp->phys[0], 5); + if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) { + full_duplex = 1; + if (epic_debug > 1) + printk("%s: Setting %s-duplex based on MII xcvr %d" + " register read of %4.4x.\n", dev->name, + full_duplex ? "full" : "half", tp->phys[0], + mii_read(ioaddr, tp->phys[0], 5)); + } + + outl(full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); + outl(virt_to_bus(tp->rx_ring), ioaddr + PRxCDAR); + outl(virt_to_bus(tp->tx_ring), ioaddr + PTxCDAR); + + /* Start the chip's Rx process. */ + set_rx_mode(dev); + outl(0x000A, ioaddr + COMMAND); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Enable interrupts by setting the interrupt mask. */ + outl(CntFull | TxUnderrun | TxDone + | RxError | RxOverflow | RxFull | RxHeader | RxDone, + ioaddr + INTMASK); + + if (epic_debug > 1) + printk("%s: epic_open() ioaddr %4.4x IRQ %d status %4.4x %s-duplex.\n", + dev->name, ioaddr, dev->irq, inl(ioaddr + GENCTL), + full_duplex ? "full" : "half"); + + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.data = (unsigned long)dev; + tp->timer.function = &epic_timer; /* timer handler */ + add_timer(&tp->timer); + + return 0; +} + +static void epic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct epic_private *tp = (struct epic_private *)dev->priv; + int ioaddr = dev->base_addr; + int next_tick = 0; + + if (epic_debug > 3) { + printk("%s: Media selection tick, Tx status %8.8x.\n", + dev->name, inl(ioaddr + TxSTAT)); + printk("%s: Other registers are IntMask %4.4x IntStatus %4.4x RxStatus" + " %4.4x.\n", + dev->name, inl(ioaddr + INTMASK), inl(ioaddr + INTSTAT), + inl(ioaddr + RxSTAT)); + } + + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void epic_tx_timeout(struct device *dev) +{ + struct epic_private *tp = (struct epic_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (epic_debug > 0) { + printk("%s: Transmit timeout using MII device, Tx status %4.4x.\n", + dev->name, inw(ioaddr + TxSTAT)); + if (epic_debug > 1) { + printk("%s: Tx indices: dirty_tx %d, cur_tx %d.\n", + dev->name, tp->dirty_tx, tp->cur_tx); + } + } + /* Perhaps stop and restart the chip's Tx processes . */ + /* Trigger a transmit demand. */ + outl(0x0004, dev->base_addr + COMMAND); + + dev->trans_start = jiffies; + tp->stats.tx_errors++; + return; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +epic_init_ring(struct device *dev) +{ + struct epic_private *tp = (struct epic_private *)dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + tp->rx_ring[i].status = 0x8000; /* Owned by Epic chip */ + tp->rx_ring[i].buflength = PKT_BUF_SZ; + { + /* Note the receive buffer must be longword aligned. + 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); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; /* Bad news! */ + skb->dev = dev; /* Mark as being used by this device. */ + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ + tp->rx_ring[i].bufaddr = virt_to_bus(skb->tail); + } + tp->rx_ring[i].next = virt_to_bus(&tp->rx_ring[i+1]); + } + /* Mark the last entry as wrapping the ring. */ + tp->rx_ring[i-1].next = virt_to_bus(&tp->rx_ring[0]); + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_ring[i].status = 0x0000; + tp->tx_ring[i].next = virt_to_bus(&tp->tx_ring[i+1]); + } + tp->tx_ring[i-1].next = virt_to_bus(&tp->tx_ring[0]); +} + +static int +epic_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct epic_private *tp = (struct epic_private *)dev->priv; + int entry; + u32 flag; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + epic_tx_timeout(dev); + return 1; + } + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + tp->tx_skbuff[entry] = skb; + tp->tx_ring[entry].txlength = (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN); + tp->tx_ring[entry].bufaddr = virt_to_bus(skb->data); + tp->tx_ring[entry].buflength = skb->len; + + if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ + flag = 0x10; /* No interrupt */ + clear_bit(0, (void*)&dev->tbusy); + } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { + flag = 0x14; /* Tx-done intr. */ + clear_bit(0, (void*)&dev->tbusy); + } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { + flag = 0x10; /* No Tx-done intr. */ + clear_bit(0, (void*)&dev->tbusy); + } else { + /* Leave room for two additional entries. */ + flag = 0x14; /* Tx-done intr. */ + tp->tx_full = 1; + } + + tp->tx_ring[entry].control = flag; + tp->tx_ring[entry].status = 0x8000; /* Pass ownership to the chip. */ + tp->cur_tx++; + /* Trigger an immediate transmit demand. */ + outl(0x0004, dev->base_addr + COMMAND); + + dev->trans_start = jiffies; + if (epic_debug > 4) + printk("%s: Queued Tx packet size %d to slot %d, " + "flag %2.2x Tx status %8.8x.\n", + dev->name, (int)skb->len, entry, flag, + inl(dev->base_addr + TxSTAT)); + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct device *dev = (struct device *)dev_instance; + struct epic_private *lp; + int status, ioaddr, boguscnt = max_interrupt_work; + + ioaddr = dev->base_addr; + lp = (struct epic_private *)dev->priv; + if (dev->interrupt) { + printk("%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; + + do { + status = inl(ioaddr + INTSTAT); + /* Acknowledge all of the current interrupt sources ASAP. */ + outl(status & 0x00007fff, ioaddr + INTSTAT); + + if (epic_debug > 4) + printk("%s: interrupt interrupt=%#8.8x new intstat=%#8.8x.\n", + dev->name, status, inl(ioaddr + INTSTAT)); + + if ((status & (RxDone | TxEmpty | TxDone)) == 0) + break; + + if (status & RxDone) /* Rx interrupt */ + epic_rx(dev); + + if (status & (TxEmpty | TxDone)) { + int dirty_tx; + + for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + int txstatus = lp->tx_ring[entry].status; + + if (txstatus < 0) + break; /* It still hasn't been Txed */ + + if ( ! (txstatus & 0x0001)) { + /* There was an major error, log it. */ +#ifndef final_version + if (epic_debug > 1) + printk("%s: Transmit error, Tx status %8.8x.\n", + dev->name, txstatus); +#endif + lp->stats.tx_errors++; + if (txstatus & 0x1050) lp->stats.tx_aborted_errors++; + if (txstatus & 0x0008) lp->stats.tx_carrier_errors++; + if (txstatus & 0x0040) lp->stats.tx_window_errors++; + if (txstatus & 0x0010) lp->stats.tx_fifo_errors++; +#ifdef ETHER_STATS + if (txstatus & 0x1000) lp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if ((txstatus & 0x0002) != 0) lp->stats.tx_deferred++; +#endif + lp->stats.collisions += (txstatus >> 8) & 15; + lp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (lp->tx_full && dev->tbusy + && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + /* The ring is no longer full, clear tbusy. */ + lp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + + lp->dirty_tx = dirty_tx; + } + + /* Check uncommon events all at once. */ + if (status & (CntFull | TxUnderrun | RxOverflow)) { + /* Always update the error counts to avoid overhead later. */ + lp->stats.rx_missed_errors += inb(ioaddr + MPCNT); + lp->stats.rx_frame_errors += inb(ioaddr + ALICNT); + lp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + + if (status & TxUnderrun) { /* Tx FIFO underflow. */ + lp->stats.tx_fifo_errors++; + /* Restart the transmit process. */ + outl(0x0080, ioaddr + COMMAND); + } + if (status & RxOverflow) { /* Missed a Rx frame. */ + lp->stats.rx_errors++; + } + /* Clear all error sources. */ + outl(status & 0x7f18, ioaddr + INTSTAT); + } + if (--boguscnt < 0) { + printk("%s: Too much work at interrupt, IntrStatus=0x%8.8x.\n", + dev->name, status); + /* Clear all interrupt sources. */ + outl(0x0001ffff, ioaddr + INTSTAT); + break; + } + } while (1); + + if (epic_debug > 3) + printk("%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, inl(ioaddr + INTSTAT)); + + dev->interrupt = 0; + return; +} + +static int +epic_rx(struct device *dev) +{ + struct epic_private *lp = (struct epic_private *)dev->priv; + int entry = lp->cur_rx % RX_RING_SIZE; + + if (epic_debug > 4) + printk(" In epic_rx(), entry %d %8.8x.\n", entry, + lp->rx_ring[entry].status); + /* If we own the next entry, it's a new packet. Send it up. */ + while (lp->rx_ring[entry].status >= 0) { + int status = lp->rx_ring[entry].status; + + if (epic_debug > 4) + printk(" epic_rx() status was %8.8x.\n", status); + if (status & 0x2000) { + printk("%s: Oversized Ethernet frame spanned multiple buffers," + " status %4.4x!\n", dev->name, status); + lp->stats.rx_length_errors++; + } else if (status & 0x0006) { + /* Rx Frame errors are counted in hardware. */ + lp->stats.rx_errors++; + } else { + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + short pkt_len = lp->rx_ring[entry].rxlength - 4; + 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 = lp->rx_skbuff[entry]; + temp = skb_put(skb, pkt_len); + if (bus_to_virt(lp->rx_ring[entry].bufaddr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in epic_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(lp->rx_ring[entry].bufaddr), + skb->head, temp); + /* Get a fresh skbuff to replace the filled one. */ + newskb = dev_alloc_skb(PKT_BUF_SZ); + if (newskb) { + rx_in_place = 1; + lp->rx_skbuff[entry] = newskb; + newskb->dev = dev; + /* Align IP on 16 byte boundaries */ + skb_reserve(newskb, 2); + lp->rx_ring[entry].bufaddr = virt_to_bus(newskb->tail); + } else /* No memory, drop the packet. */ + skb = 0; + } else + skb = dev_alloc_skb(pkt_len + 2); + if (skb == NULL) { + int i; + printk("%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++. */ + for (i = 0; i < RX_RING_SIZE; i++) + if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) + break; + + if (i > RX_RING_SIZE -2) { + lp->stats.rx_dropped++; + lp->rx_ring[entry].status = 0x8000; + lp->cur_rx++; + } + break; + } + skb->dev = dev; + if (! rx_in_place) { + skb_reserve(skb, 2); /* 16 byte align the data fields */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(lp->rx_ring[entry].bufaddr), pkt_len); + } + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + lp->stats.rx_packets++; + } + + lp->rx_ring[entry].status = 0x8000; + entry = (++lp->cur_rx) % RX_RING_SIZE; + } + + return 0; +} + +static int +epic_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct epic_private *tp = (struct epic_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (epic_debug > 1) + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inl(ioaddr + INTSTAT)); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0x00000000, ioaddr + INTMASK); + /* Stop the chip's Tx and Rx DMA processes. */ + outw(0x0061, ioaddr + COMMAND); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += inb(ioaddr + MPCNT); + tp->stats.rx_frame_errors += inb(ioaddr + ALICNT); + tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + + del_timer(&tp->timer); + + free_irq(dev->irq, dev); + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = tp->rx_skbuff[i]; + tp->rx_skbuff[i] = 0; + tp->rx_ring[i].status = 0; /* Not owned by Epic chip. */ + tp->rx_ring[i].buflength = 0; + tp->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */ + if (skb) { +#if LINUX_VERSION_CODE < 0x20100 + skb->free = 1; +#endif + dev_kfree_skb(skb, FREE_WRITE); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); + tp->tx_skbuff[i] = 0; + } + + + /* Green! Leave the chip in low-power mode. */ + outl(0x0008, ioaddr + GENCTL); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +epic_get_stats(struct device *dev) +{ + struct epic_private *tp = (struct epic_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (dev->start) { + /* Update the error counts. */ + tp->stats.rx_missed_errors += inb(ioaddr + MPCNT); + tp->stats.rx_frame_errors += inb(ioaddr + ALICNT); + tp->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + } + + return &tp->stats; +} + +/* 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) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + 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; +} + + +static void set_rx_mode(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct epic_private *tp = (struct epic_private *)dev->priv; + unsigned char mc_filter[8]; /* Multicast hash filter */ + int i; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + outl(0x002C, ioaddr + RxCtrl); + /* Unconditionally log net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + memset(mc_filter, 0xff, sizeof(mc_filter)); + } else if ((dev->mc_count > 0) || (dev->flags & IFF_ALLMULTI)) { + /* There is apparently a chip bug, so the multicast filter + is never enabled. */ + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + outl(0x000C, ioaddr + RxCtrl); + } else if (dev->mc_count == 0) { + outl(0x0004, ioaddr + RxCtrl); + return; + } else { /* Never executed, for now. */ + struct dev_mc_list *mclist; + + memset(mc_filter, 0, sizeof(mc_filter)); + 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); + } + /* 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 < 4; i++) + outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4); + memcpy(tp->mc_filter, mc_filter, sizeof(mc_filter)); + } + return; +} + + +#ifdef CARDBUS + +#include + +static dev_node_t *epic_attach(dev_locator_t *loc) +{ + struct device *dev; + u16 dev_id; + u32 io; + u8 bus, devfn, irq; + + if (loc->bus != LOC_PCI) return NULL; + bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; + printk(KERN_INFO "epic_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_DEVICE_ID, &dev_id); + io &= ~3; + dev = epic100_probe1(bus, devfn, NULL, -1); + if (dev) { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + strcpy(node->dev_name, dev->name); + node->major = node->minor = 0; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } + return NULL; +} + +static void epic_detach(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "epic_detach(%s)\n", node->dev_name); + for (devp = &root_epic_dev; *devp; devp = next) { + next = &((struct epic_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + unregister_netdev(*devp); + kfree(*devp); + *devp = *next; + kfree(node); + MOD_DEC_USE_COUNT; + } +} + +struct driver_operations epic_ops = { + "epic_cb", epic_attach, NULL, NULL, epic_detach +}; + +#endif /* Cardbus support */ + + +#ifdef MODULE + +/* An additional parameter that may be passed in... */ +static int debug = -1; + +int +init_module(void) +{ + if (debug >= 0) + epic_debug = debug; + +#ifdef CARDBUS + register_driver(&epic_ops); + return 0; +#else + return epic100_probe(0); +#endif +} + +void +cleanup_module(void) +{ + struct device *next_dev; + +#ifdef CARDBUS + unregister_driver(&epic_ops); +#endif + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_epic_dev) { + next_dev = ((struct epic_private *)root_epic_dev->priv)->next_module; + unregister_netdev(root_epic_dev); + release_region(root_epic_dev->base_addr, EPIC_TOTAL_SIZE); + kfree(root_epic_dev); + root_epic_dev = next_dev; + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c" + * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic100.c" + * alt-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c epic.c -o epic_cb.c -I/usr/src/pcmcia-cs-3.0.0/include/" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/eql.c linux/drivers/net/eql.c --- v2.0.33/linux/drivers/net/eql.c Tue Aug 12 14:15:56 1997 +++ linux/drivers/net/eql.c Wed Jun 3 15:17:47 1998 @@ -375,7 +375,6 @@ equalizer_t *eql = (equalizer_t *) dev->priv; struct device *slave_dev = 0; slave_t *slave; - struct sk_buff *skb2; if (skb == NULL) return 0; diff -u --recursive --new-file v2.0.33/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c --- v2.0.33/linux/drivers/net/eth16i.c Fri Aug 15 15:35:39 1997 +++ linux/drivers/net/eth16i.c Wed Jun 3 15:17:47 1998 @@ -332,7 +332,7 @@ static int eth16i_tx(struct sk_buff *skb, struct device *dev); static void eth16i_rx(struct device *dev); static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs); +static void eth16i_multicast(struct device *dev); static void eth16i_select_regbank(unsigned char regbank, short ioaddr); static void eth16i_initialize(struct device *dev); static struct enet_statistics *eth16i_get_stats(struct device *dev); @@ -1152,7 +1152,7 @@ return; } -static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs) +static void eth16i_multicast(struct device *dev) { short ioaddr = dev->base_addr; diff -u --recursive --new-file v2.0.33/linux/drivers/net/hp100.c linux/drivers/net/hp100.c --- v2.0.33/linux/drivers/net/hp100.c Tue Aug 5 09:11:35 1997 +++ linux/drivers/net/hp100.c Wed Jun 3 15:17:47 1998 @@ -2,7 +2,7 @@ ** hp100.c ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters ** -** $Id: hp100.c,v 1.54 1997/06/12 10:37:07 perex Exp perex $ +** $Id: hp100.c,v 1.56 1998/03/04 15:23:59 perex Exp perex $ ** ** Based on the HP100 driver written by Jaroslav Kysela ** Extended for new busmaster capable chipsets by @@ -17,6 +17,7 @@ ** -- HP J2973 10 Mbit/s PCI 10base-T ** -- HP J2573 10/100 ISA ** -- Compex ReadyLink ENET100-VG4 10/100 Mbit/s PCI / EISA +** -- Compex FreedomLine 100/VG 10/100 Mbit/s ISA / EISA / PCI ** ** but it should also work with the other CASCADE based adapters. ** @@ -27,6 +28,7 @@ ** - To reduce interrupt load in busmaster, one could switch off ** the interrupts that are used to refill the queues whenever the ** queues are filled up to more than a certain threshold. +** - some updates for EISA version of card ** ** ** This source/code is public free; you can distribute it and/or modify @@ -34,6 +36,18 @@ ** Free Software Foundation) either version two of this License, or any ** later version. ** +** 1.55 -> 1.56 +** - removed printk in misc. interrupt and update statistics to allow +** monitoring of card status +** - timing changes in xmit routines, relogin to 100VG hub added when +** driver does reset +** - included fix for Compex FreedomLine PCI adapter +** +** 1.54 -> 1.55 +** - fixed bad initialization in init_module +** - added Compex FreedomLine adapter +** - some fixes in card initialization +** ** 1.53 -> 1.54 ** - added hardware multicast filter support (doesn't work) ** - little changes in hp100_sense_lan routine @@ -115,6 +129,12 @@ #ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4 #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 #endif +#ifndef PCI_VENDOR_ID_COMPEX2 +#define PCI_VENDOR_ID_COMPEX2 0x101a +#endif +#ifndef PCI_DEVICE_ID_COMPEX2_100VG +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 +#endif #define HP100_REGION_SIZE 0x20 /* for ioports */ @@ -141,11 +161,17 @@ u_char bus; }; +struct hp100_pci_id { + u_short vendor; + u_short device; +}; + struct hp100_private { struct hp100_eisa_id *id; u_short chip; u_short soft_model; u_int memory_size; + u_int virt_memory_size; u_short rx_ratio; /* 1 - 99 */ u_short priority_tx; /* != 0 - priority tx */ u_short mode; /* PIO, Shared Mem or Busmaster */ @@ -213,10 +239,35 @@ /* 10/100 EISA card from Compex */ { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + /* 10/100 EISA card from Compex - FreedomLine (sq5bpf) */ + /* Note: plhbrod@mbox.vol.cz reported that same ID have ISA */ + /* version of adapter, too... */ + { 0x0104180e, "FreedomLine 100/VG", HP100_BUS_EISA }, + + /* 10/100 PCI card from Compex - FreedomLine + * + * I think this card doesn't like aic7178 scsi controller, but + * I haven't tested this much. It works fine on diskless machines. + * Jacek Lipkowski + */ + { 0x021211f6, "FreedomLine 100/VG", HP100_BUS_PCI }, + /* 10/100 PCI card from Compex (J2585A compatible) */ { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } + }; +#define HP100_EISA_IDS_SIZE (sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id)) + +static struct hp100_pci_id hp100_pci_ids[] = { + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B }, + { PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4 }, + { PCI_VENDOR_ID_COMPEX2, PCI_DEVICE_ID_COMPEX2_100VG } +}; + +#define HP100_PCI_IDS_SIZE (sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id)) + static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; static int hp100_mode = 1; @@ -238,13 +289,14 @@ static int hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); static void hp100_rx( struct device *dev ); static hp100_stats_t *hp100_get_stats( struct device *dev ); +static void hp100_misc_interrupt( struct device *dev ); static void hp100_update_stats( struct device *dev ); static void hp100_clear_stats( int ioaddr ); static void hp100_set_multicast_list( struct device *dev); static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); static void hp100_start_interface( struct device *dev ); static void hp100_stop_interface( struct device *dev ); -static void hp100_load_eeprom( struct device *dev ); +static void hp100_load_eeprom( struct device *dev, u_short ioaddr ); static int hp100_sense_lan( struct device *dev ); static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); static int hp100_down_vg_link( struct device *dev ); @@ -272,7 +324,7 @@ * These functions should - if possible - avoid doing write operations * since this could cause problems when the card is not installed. */ - + __initfunc(int hp100_probe( struct device *dev )) { int base_addr = dev ? dev -> base_addr : 0; @@ -322,17 +374,16 @@ { u_char pci_bus, pci_device_fn; u_short pci_command; + int pci_id_index; - if ((pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) && - (pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) && - (pcibios_find_device( PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4, - pci_index, &pci_bus, - &pci_device_fn ) != 0 ) ) break; + for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ ) + if ( pcibios_find_device( hp100_pci_ids[ pci_id_index ].vendor, + hp100_pci_ids[ pci_id_index ].device, + pci_index, &pci_bus, + &pci_device_fn ) == 0 ) goto __pci_found; + break; + __pci_found: pcibios_read_config_dword( pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &ioaddr ); @@ -342,6 +393,15 @@ pcibios_read_config_word( pci_bus, pci_device_fn, PCI_COMMAND, &pci_command ); + if ( !( pci_command & PCI_COMMAND_IO ) ) + { +#ifdef HP100_DEBUG + printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name ); +#endif + pci_command |= PCI_COMMAND_IO; + pcibios_write_config_word( pci_bus, pci_device_fn, + PCI_COMMAND, pci_command ); + } if ( !( pci_command & PCI_COMMAND_MASTER ) ) { #ifdef HP100_DEBUG @@ -386,7 +446,7 @@ u_char uc, uc_1; u_int eisa_id; u_int chip; - u_int memory_size = 0; + u_int memory_size = 0, virt_memory_size = 0; u_short local_mode, lsw; short mem_mapped; u_int *mem_ptr_phys, *mem_ptr_virt; @@ -443,14 +503,18 @@ return -ENODEV; } - for ( i=0; i= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) ) - { + if ( i >= HP100_EISA_IDS_SIZE ) { + for ( i = 0; i < HP100_EISA_IDS_SIZE; i++) + if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) ) + break; + if ( i >= HP100_EISA_IDS_SIZE ) { printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id ); - return -ENODEV; + return -ENODEV; } + } eid = &hp100_eisa_ids[ i ]; if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) { @@ -468,7 +532,13 @@ return -EIO; } - /* Determine driver operation mode + /* Make sure, that all registers are correctly updated... */ + + hp100_load_eeprom( dev, ioaddr ); + wait(); + + /* + * Determine driver operation mode * * Use the variable "hp100_mode" upon insmod or as kernel parameter to * force driver modes: @@ -478,6 +548,22 @@ * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. */ + /* + * LSW values: + * 0x2278 -> J2585B, PnP shared memory mode + * 0x2270 -> J2585B, shared memory mode, 0xdc000 + * 0xa23c -> J2585B, I/O mapped mode + * 0x2240 -> EISA COMPEX, BusMaster (Shasta Chip) + * 0x2220 -> EISA HP, I/O (Shasta Chip) + * 0x2260 -> EISA HP, BusMaster (Shasta Chip) + */ + +#if 0 + local_mode = 0x2270; + hp100_outw(0xfefe,OPTION_LSW); + hp100_outw(local_mode|HP100_SET_LB|HP100_SET_HB,OPTION_LSW); +#endif + /* hp100_mode value maybe used in future by another card */ local_mode=hp100_mode; if ( local_mode < 1 || local_mode > 4 ) @@ -536,7 +622,8 @@ else { #ifdef HP100_DEBUG - printk("hp100: %s: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n", dev->name); + printk("hp100: %s: Card not configured for BM or BM not supported with this card.\n", dev->name ); + printk("hp100: %s: Trying shared memory mode.\n", dev->name); #endif /* In this case, try shared memory mode */ local_mode=2; @@ -554,6 +641,7 @@ mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); mem_ptr_phys = mem_ptr_virt = NULL; memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); + virt_memory_size = 0; /* For memory mapped or busmaster mode, we want the memory address */ if ( mem_mapped || (local_mode==1)) @@ -574,21 +662,21 @@ /* However in slave mode we need to remap high (>1GB) card memory */ if(local_mode!=1) /* = not busmaster */ { - if ( bus == HP100_BUS_PCI ) + if ( bus == HP100_BUS_PCI && mem_ptr_phys >= (u_int *)0x100000 ) { /* We try with smaller memory sizes, if ioremap fails */ - for(; memory_size>16383; memory_size=memory_size/2) + for(virt_memory_size = memory_size; virt_memory_size>16383; virt_memory_size>>=1) { - if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,memory_size))==NULL) + if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,virt_memory_size))==NULL) { #ifdef HP100_DEBUG - printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, memory_size, (u_long)mem_ptr_phys ); + printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys ); #endif } else { #ifdef HP100_DEBUG - printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); + printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); #endif break; } @@ -598,7 +686,7 @@ { printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name); local_mode=3; - memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07) ); + virt_memory_size = 0; } } } @@ -637,6 +725,7 @@ dev->base_addr = ioaddr; lp->memory_size = memory_size; + lp->virt_memory_size = virt_memory_size; lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ /* memory region for programmed i/o */ @@ -724,7 +813,8 @@ if ( lp->mode==2 ) /* memory mapped */ { printk( "hp100: %s: Memory area at 0x%lx-0x%lx", - dev->name,(u_long)mem_ptr_phys,(u_long)mem_ptr_phys+(u_long)lp->memory_size ); + dev->name,(u_long)mem_ptr_phys, + ((u_long)mem_ptr_phys+(mem_ptr_phys>(u_int *)0x100000?(u_long)lp->memory_size:16*1024))-1 ); if ( mem_ptr_virt ) printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); printk( ".\n" ); @@ -786,7 +876,7 @@ } /* Initiate EEPROM reload */ - hp100_load_eeprom( dev ); + hp100_load_eeprom( dev, 0 ); wait(); @@ -1447,6 +1537,7 @@ /* tx function for busmaster mode */ static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) { + unsigned long flags; int i, ok_flag; int ioaddr = dev->base_addr; struct hp100_private *lp = (struct hp100_private *)dev->priv; @@ -1459,7 +1550,9 @@ if ( skb==NULL ) { +#ifndef LINUX_2_1 dev_tint( dev ); +#endif return 0; } @@ -1473,7 +1566,7 @@ printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name); #endif /* not waited long enough since last tx? */ - if ( jiffies - dev->trans_start < HZ/10 ) return -EAGAIN; + if ( jiffies - dev->trans_start < HZ ) return -EAGAIN; if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ { @@ -1519,6 +1612,8 @@ { printk( "hp100: %s: interface reset\n", dev->name ); hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); hp100_start_interface( dev ); } } @@ -1531,6 +1626,7 @@ * we have to turn int's off before modifying this, otherwise * a tx_pdl_cleanup could occur at the same time */ + save_flags( flags ); cli(); ringptr=lp->txrtail; lp->txrtail=ringptr->next; @@ -1557,7 +1653,7 @@ hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ lp->txrcommit++; - sti(); + restore_flags( flags ); /* Update statistics */ lp->stats.tx_packets++; @@ -1606,7 +1702,11 @@ hp100_inb(TX_PDL), donecount); #endif +#ifdef LINUX_2_1 + dev_kfree_skb( lp->txrhead->skb ); +#else dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); +#endif lp->txrhead->skb=(void *)NULL; lp->txrhead=lp->txrhead->next; lp->txrcommit--; @@ -1627,6 +1727,16 @@ printk("hp100: %s: start_xmit\n", dev->name); #endif + if ( skb==NULL ) + { +#ifndef LINUX_2_1 + dev_tint( dev ); +#endif + return 0; + } + + if ( skb->len <= 0 ) return 0; + if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ { hp100_stop_interface( dev ); @@ -1649,7 +1759,7 @@ printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i ); #endif /* not waited long enough since last failed tx try? */ - if ( jiffies - dev->trans_start < HZ/2 ) + if ( jiffies - dev->trans_start < HZ ) { #ifdef HP100_DEBUG printk("hp100: %s: trans_start timing problem\n", dev->name); @@ -1686,6 +1796,8 @@ { printk( "hp100: %s: interface reset\n", dev->name ); hp100_stop_interface( dev ); + if ( lp->lan_type == HP100_LAN_100 ) + lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); hp100_start_interface( dev ); udelay(1000); } @@ -1721,13 +1833,14 @@ if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ { /* Note: The J2585B needs alignment to 32bits here! */ - memcpy( lp->mem_ptr_virt, skb->data, ( skb->len +3 ) & ~3 ); + memcpy( lp->mem_ptr_virt, skb->data, ( skb->len + 3 ) & ~3 ); if ( !ok_flag ) memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); } else { - memcpy_toio( lp->mem_ptr_phys, skb->data, skb->len ); + /* Note: The J2585B needs alignment to 32bits here! */ + memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 ); if ( !ok_flag ) memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); } @@ -1749,7 +1862,11 @@ dev->trans_start=jiffies; hp100_ints_on(); +#ifdef LINUX_2_1 + dev_kfree_skb( skb ); +#else dev_kfree_skb( skb, FREE_WRITE ); +#endif #ifdef HP100_DEBUG_TX printk( "hp100: %s: start_xmit: end\n", dev->name ); @@ -1814,7 +1931,7 @@ else /* programmed i/o */ header = hp100_inl( DATA32 ); - pkt_len = header & HP100_PKT_LEN_MASK; + pkt_len = ((header & HP100_PKT_LEN_MASK) + 3) & ~3; #ifdef HP100_DEBUG_RX printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", @@ -1824,10 +1941,6 @@ #endif /* Now we allocate the skb and transfer the data into it. */ - /* NOTE! This (and the skb_put() below) depends on the skb-functions - * allocating more than asked (notably, aligning the request up to - * the next 16-byte length). - */ skb = dev_alloc_skb( pkt_len ); if ( skb == NULL ) /* Not enough memory->drop packet */ { @@ -1849,13 +1962,13 @@ if ( lp->mode==2 ) { if ( lp->mem_ptr_virt ) - memcpy( ptr, lp->mem_ptr_virt, ( pkt_len + 3 ) & ~3 ); + memcpy( ptr, lp->mem_ptr_virt, pkt_len ); /* Note alignment to 32bit transfers */ else - memcpy_fromio( ptr, lp->mem_ptr_phys, ( pkt_len + 3 ) & ~3 ); + memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len ); } else /* io mapped */ - insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 ); + insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 ); skb->protocol = eth_type_trans( skb, dev ); @@ -1987,7 +2100,11 @@ printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len); #endif if(ptr->skb!=NULL) +#ifdef LINUX_2_1 + dev_kfree_skb( ptr->skb ); +#else dev_kfree_skb( ptr->skb, FREE_READ ); +#endif lp->stats.rx_errors++; } @@ -2055,20 +2172,37 @@ hp100_page( PERFORMANCE ); } +static void hp100_misc_interrupt( struct device *dev ) +{ + struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B + hp100_outw( 0x4216, TRACE ); + printk("hp100: %s: misc_interrupt\n", dev->name); +#endif + + /* Note: Statistics counters clear when read. */ + lp->stats.rx_errors++; + lp->stats.tx_errors++; +} + static void hp100_clear_stats( int ioaddr ) { + unsigned long flags; + #ifdef HP100_DEBUG_B hp100_outw( 0x4217, TRACE ); printk("hp100: %s: clear_stats\n", dev->name); #endif + save_flags( flags ); cli(); hp100_page( MAC_CTRL ); /* get all statistics bytes */ hp100_inw( DROPPED ); hp100_inb( CRC ); hp100_inb( ABORT ); hp100_page( PERFORMANCE ); - sti(); + restore_flags( flags ); } @@ -2080,8 +2214,9 @@ * Set or clear the multicast filter for this adapter. */ -static void hp100_set_multicast_list( struct device *dev) +static void hp100_set_multicast_list( struct device *dev ) { + unsigned long flags; int ioaddr = dev->base_addr; struct hp100_private *lp = (struct hp100_private *)dev->priv; @@ -2090,6 +2225,7 @@ printk("hp100: %s: set_mc_list\n", dev->name); #endif + save_flags( flags ); cli(); hp100_ints_off(); hp100_page( MAC_CTRL ); @@ -2223,7 +2359,7 @@ hp100_page( PERFORMANCE ); hp100_ints_on(); - sti(); + restore_flags( flags ); } @@ -2354,12 +2490,15 @@ */ if ( val & HP100_MISC_ERROR ) /* New for J2585B */ { +#ifdef HP100_DEBUG_IRQ printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name); +#endif if(lp->mode==1) { hp100_clean_txring( dev ); hp100_rxfill( dev ); } + hp100_misc_interrupt( dev ); } dev->interrupt = 0; @@ -2373,6 +2512,7 @@ static void hp100_start_interface( struct device *dev ) { + unsigned long flags; int ioaddr = dev->base_addr; struct hp100_private *lp = (struct hp100_private *)dev->priv; @@ -2381,6 +2521,7 @@ printk("hp100: %s: hp100_start_interface\n",dev->name); #endif + save_flags( flags ); cli(); /* Ensure the adapter does not want to request an interrupt when */ @@ -2430,8 +2571,9 @@ } /* Enable MAC Tx and RX, set MAC modes, ... */ - /* Note: This function also turns on the interrupts. */ hp100_set_multicast_list( dev ); + + restore_flags( flags ); } @@ -2473,10 +2615,10 @@ } -static void hp100_load_eeprom( struct device *dev ) +static void hp100_load_eeprom( struct device *dev, u_short probe_ioaddr ) { int i; - int ioaddr = dev->base_addr; + int ioaddr = probe_ioaddr > 0 ? probe_ioaddr : dev->base_addr; #ifdef HP100_DEBUG_B hp100_outw( 0x4222, TRACE ); @@ -2915,14 +3057,13 @@ int init_module( void ) { - int i; - int ret = 0; + int i, cards; if (hp100_port == 0 && !EISA_bus && !pcibios_present()) printk("hp100: You should not use auto-probing with insmod!\n"); /* Loop on all possible base addresses */ - i = -1; + i = -1; cards = 0; while((hp100_port[++i] != -1) && (i < 5)) { /* Create device and set basics args */ @@ -2939,11 +3080,12 @@ /* Note: if dev->priv is mallocated, there is no way to fail */ kfree_s(hp100_devlist[i], sizeof(struct device)); hp100_devlist[i] = (struct device *) NULL; - ret = -EIO; } + else + cards++; } /* Loop over all devices */ - return ret; + return cards > 0 ? 0 : -ENODEV; } void cleanup_module( void ) diff -u --recursive --new-file v2.0.33/linux/drivers/net/lance.c linux/drivers/net/lance.c --- v2.0.33/linux/drivers/net/lance.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/lance.c Wed Jun 3 15:17:47 1998 @@ -1,6 +1,6 @@ -/* lance.c: An AMD LANCE ethernet driver for linux. */ +/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */ /* - Written 1993,1994,1995 by Donald Becker. + Written/copyright 1993-1998 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. @@ -8,13 +8,12 @@ of the GNU Public License, incorporated herein by reference. This driver is for the Allied Telesis AT1500 and HP J2405A, and should work - with most other LANCE-based bus-master (NE2100 clone) ethercards. + with most other LANCE-based bus-master (NE2100/NE2500) ethercards. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - Fixing alignment problem with 1.3.* kernel and some minor changes by Andrey V. Savochkin, 1996. @@ -28,12 +27,33 @@ But I should to inform you that I'm not an expert in the LANCE card and it may occurs that you will receive no answer on your mail to Donald Becker. I didn't receive any answer on all my letters - to him. Who knows why... But may be you are more lucky? ;-) + to him. Who knows why... But may be you are more lucky? ;-> SAW - Fixed 7990 autoIRQ failure and reversed unneeded alignment. 8/20/96 djb + + Thomas Bogendoerfer (tsbogend@bigbug.franken.de): + - added support for Linux/Alpha, but removed most of it, because + it worked only for the PCI chip. + - added hook for the 32bit lance driver + - added PCnetPCI II (79C970A) to chip table + Paul Gortmaker (gpg109@rsphy1.anu.edu.au): + - hopefully fix above so Linux/Alpha can use ISA cards too. + 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb + v1.12 10/27/97 Module support -djb + v1.14 2/3/98 Module support modified, made PCI support optional -djb */ -static const char *version = "lance.c:v1.09 Aug 20 1996 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; +static const char *version = "lance.c:v1.14 2/3/1998 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; + +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include @@ -54,8 +74,9 @@ #include #include -static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0}; -void lance_probe1(int ioaddr); +static unsigned int lance_portlist[] = { 0x300, 0x320, 0x340, 0x360, 0}; +int lance_probe(struct device *dev); +int lance_probe1(struct device *dev, int ioaddr, int irq, int options); #ifdef HAVE_DEVLIST struct netdev_entry lance_drv = @@ -96,7 +117,7 @@ probed for by enabling each free DMA channel in turn and checking if initialization succeeds. -The HP-J2405A board is an exception: with this board it's easy to read the +The HP-J2405A board is an exception: with this board it is easy to read the EEPROM-set values for the base, IRQ, and DMA. (Of course you must already _know_ the base address -- that field is for writing the EEPROM.) @@ -155,26 +176,11 @@ */ -/* Memory accessed from LANCE card must be aligned on 8-byte boundaries. - But we can't believe that kmalloc()'ed memory satisfies it. -- SAW */ -#define LANCE_KMALLOC(x) \ - ((void *) (((unsigned long)kmalloc((x)+7, GFP_DMA | GFP_KERNEL)+7) & ~7)) - -/* - * Changes: - * Thomas Bogendoerfer (tsbogend@bigbug.franken.de): - * - added support for Linux/Alpha, but removed most of it, because - * it worked only for the PCI chip. - * - added hook for the 32bit lance driver - * - added PCnetPCI II (79C970A) to chip table - * - * Paul Gortmaker (gpg109@rsphy1.anu.edu.au): - * - hopefully fix above so Linux/Alpha can use ISA cards too. - */ - /* Set the number of Tx and Rx buffers, using Log_2(# buffers). Reasonable default values are 16 Tx buffers, and 16 Rx buffers. - That translates to 4 and 4 (16 == 2^^4). */ + That translates to 4 and 4 (16 == 2^^4). + This is a compile-time option for efficiency. + */ #ifndef LANCE_LOG_TX_BUFFERS #define LANCE_LOG_TX_BUFFERS 4 #define LANCE_LOG_RX_BUFFERS 4 @@ -228,6 +234,8 @@ const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ /* Tx low-memory "bounce buffer" address. */ char (*tx_bounce_buffs)[PKT_BUF_SZ]; @@ -299,15 +307,74 @@ -/* This lance probe is unlike the other board probes in 1.0.*. The LANCE may - have to allocate a contiguous low-memory region for bounce buffers. - This requirement is satisfied by having the lance initialization occur - before the memory management system is started, and thus well before the - other probes. */ +#ifdef MODULE +#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */ +#define IF_NAMELEN 8 /* # of chars for storing dev->name */ + +static int io[MAX_CARDS] = { 0, }; +static int dma[MAX_CARDS] = { 0, }; +static int irq[MAX_CARDS] = { 0, }; + +static char ifnames[MAX_CARDS][IF_NAMELEN] = { {0, }, }; +static struct device dev_lance[MAX_CARDS] = +{{ + 0, /* device name is inserted by linux/drivers/net/net_init.c */ + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL}}; + +int init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { + struct device *dev = &dev_lance[this_dev]; + dev->name = ifnames[this_dev]; + dev->irq = irq[this_dev]; + dev->base_addr = io[this_dev]; + dev->dma = dma[this_dev]; + dev->init = lance_probe; + if (io[this_dev] == 0) { + if (this_dev != 0) break; /* only complain once */ + printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); + return -EPERM; + } + if (register_netdev(dev) != 0) { + printk(KERN_WARNING "lance.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]); + if (found != 0) return 0; /* Got at least one. */ + return -ENXIO; + } + found++; + } + + return 0; +} -int lance_init(void) +void +cleanup_module(void) { - int *port; + int this_dev; + + for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { + struct device *dev = &dev_lance[this_dev]; + if (dev->priv != NULL) { + kfree(dev->priv); + dev->priv = NULL; + free_irq(dev->irq, NULL); + release_region(dev->base_addr, LANCE_TOTAL_SIZE); + unregister_netdev(dev); + } + } +} +#endif /* MODULE */ + +/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other + board probes now that kmalloc() can allocate ISA DMA-able regions. + This also allows the LANCE driver to be used as a module. + */ +int lance_probe(struct device *dev) +{ + int *port, result; if (high_memory <= 16*1024*1024) lance_need_isa_bounce_buffers = 0; @@ -346,8 +413,9 @@ } printk("Found PCnet/PCI at %#x, irq %d.\n", pci_ioaddr, pci_irq_line); - lance_probe1(pci_ioaddr); + result = lance_probe1(dev, pci_ioaddr, pci_irq_line, 0); pci_irq_line = 0; + if (!result) return 0; } } #endif /* defined(CONFIG_PCI) */ @@ -361,16 +429,17 @@ char offset15, offset14 = inb(ioaddr + 14); if ((offset14 == 0x52 || offset14 == 0x57) && - ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44)) - lance_probe1(ioaddr); + ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44)) { + result = lance_probe1(dev, ioaddr, 0, 0); + if ( !result ) return 0; + } } } - return 0; + return -ENODEV; } -void lance_probe1(int ioaddr) +int lance_probe1(struct device *dev, int ioaddr, int irq, int options) { - struct device *dev; struct lance_private *lp; short dma_channels; /* Mark spuriously-busy DMA channels */ int i, reset_val, lance_version; @@ -407,7 +476,7 @@ outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */ if (inw(ioaddr+LANCE_DATA) != 0x0004) - return; + return -ENODEV; /* Get the version of the chip. */ outw(88, ioaddr+LANCE_ADDR); @@ -420,7 +489,7 @@ if (lance_debug > 2) printk(" LANCE chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) - return; + return -ENODEV; chip_version = (chip_version >> 12) & 0xffff; for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) { if (chip_table[lance_version].id_number == chip_version) @@ -428,7 +497,9 @@ } } - dev = init_etherdev(0, 0); + /* We can't use init_etherdev() to allocate dev->priv because it must + a ISA DMA-able region. */ + dev = init_etherdev(dev, 0); dev->open = lance_open_fail; chipname = chip_table[lance_version].name; printk("%s: %s at %#3x,", dev->name, chipname, ioaddr); @@ -442,15 +513,15 @@ request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name); #ifdef CONFIG_LANCE32 - /* look if it's a PCI or VLB chip */ - if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) { - extern void lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line); + /* look if it's a PCI or VLB chip */ + if (lance_version == PCNET_PCI || lance_version == PCNET_VLB || lance_version == PCNET_PCI_II) { + extern int lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line); - lance32_probe1 (dev, chipname, pci_irq_line); - return; + return lance32_probe1 (dev, chipname, pci_irq_line); } #endif /* Make certain the data structures used by the LANCE are aligned and DMAble. */ + lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7, GFP_DMA | GFP_KERNEL)+7) & ~7); if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); @@ -484,9 +555,9 @@ outw(0x0000, ioaddr+LANCE_ADDR); inw(ioaddr+LANCE_ADDR); - if (pci_irq_line) { + if (irq) { /* Set iff PCI card. */ dev->dma = 4; /* Native bus-master, no DMA channel needed. */ - dev->irq = pci_irq_line; + dev->irq = irq; } else if (hp_builtin) { static const char dma_tbl[4] = {3, 5, 6, 0}; static const char irq_tbl[4] = {3, 4, 5, 9}; @@ -535,7 +606,7 @@ printk(", probed IRQ %d", dev->irq); else { printk(", failed to detect IRQ line.\n"); - return; + return -ENODEV; } /* Check for the initialization done bit, 0x0100, which means @@ -549,7 +620,7 @@ } else if (dev->dma) { if (request_dma(dev->dma, chipname)) { printk("DMA %d allocation failed.\n", dev->dma); - return; + return -ENODEV; } else printk(", assigned DMA %d.\n", dev->dma); } else { /* OK, we have to auto-DMA. */ @@ -584,7 +655,7 @@ } if (i == 4) { /* Failure: bail. */ printk("DMA detection failed.\n"); - return; + return -ENODEV; } } @@ -597,7 +668,7 @@ dev->irq = autoirq_report(4); if (dev->irq == 0) { printk(" Failed to detect the 7990 IRQ line.\n"); - return; + return -ENODEV; } printk(" Auto-IRQ detected IRQ%d.\n", dev->irq); } @@ -606,7 +677,8 @@ /* Turn on auto-select of media (10baseT or BNC) so that the user can watch the LEDs even if the board isn't opened. */ outw(0x0002, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); + /* Don't touch 10base2 power bit. */ + outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); } if (lance_debug > 0 && did_version++ == 0) @@ -619,7 +691,7 @@ dev->get_stats = lance_get_stats; dev->set_multicast_list = set_multicast_list; - return; + return 0; } static int @@ -638,15 +710,15 @@ int i; if (dev->irq == 0 || - request_irq(dev->irq, &lance_interrupt, 0, lp->name, NULL)) { + request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { return -EAGAIN; } + MOD_INC_USE_COUNT; + /* We used to allocate DMA here, but that was silly. DMA lines can't be shared! We now permanently allocate them. */ - irq2dev_map[dev->irq] = dev; - /* Reset the LANCE */ inw(ioaddr+LANCE_RESET); @@ -663,8 +735,9 @@ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */ outw(0x0002, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); - } + /* Only touch autoselect bit. */ + outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); + } if (lance_debug > 1) printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", @@ -745,12 +818,26 @@ lp->dirty_rx = lp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000; + struct sk_buff *skb; + void *rx_buff; + + skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL); + lp->rx_skbuff[i] = skb; + if (skb) { + skb->dev = dev; + rx_buff = skb->tail; + } else + rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | GFP_KERNEL); + if (rx_buff == NULL) + lp->rx_ring[i].base = 0; + else + lp->rx_ring[i].base = (u32)virt_to_bus(rx_buff) | 0x80000000; lp->rx_ring[i].buf_length = -PKT_BUF_SZ; } /* The Tx buffer address is filled in as needed, but we do need to clear the upper ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_skbuff[i] = 0; lp->tx_ring[i].base = 0; } @@ -816,18 +903,10 @@ dev->tbusy=0; dev->trans_start = jiffies; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - if (skb == NULL) { - dev_tint(dev); return 0; } - if (skb->len <= 0) - return 0; - if (lance_debug > 3) { outw(0x0000, ioaddr+LANCE_ADDR); printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, @@ -904,7 +983,7 @@ static void lance_interrupt(int irq, void *dev_id, struct pt_regs * regs) { - struct device *dev = (struct device *)(irq2dev_map[irq]); + struct device *dev = (struct device *)dev_id; struct lance_private *lp; int csr0, ioaddr, boguscnt=10; int must_restart; @@ -1111,6 +1190,7 @@ { int ioaddr = dev->base_addr; struct lance_private *lp = (struct lance_private *)dev->priv; + int i; dev->start = 0; dev->tbusy = 1; @@ -1132,10 +1212,25 @@ if (dev->dma != 4) disable_dma(dev->dma); - free_irq(dev->irq, NULL); + free_irq(dev->irq, dev); - irq2dev_map[dev->irq] = 0; + /* Free all the skbuffs in the Rx and Tx queues. */ + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb = lp->rx_skbuff[i]; + lp->rx_skbuff[i] = 0; + lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ + if (skb) { + skb->free = 1; + dev_kfree_skb(skb, FREE_WRITE); + } + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (lp->tx_skbuff[i]) + dev_kfree_skb(lp->tx_skbuff[i], FREE_WRITE); + lp->tx_skbuff[i] = 0; + } + MOD_DEC_USE_COUNT; return 0; } @@ -1198,8 +1293,9 @@ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c" * c-indent-level: 4 + * c-basic-offset: 4 * tab-width: 4 * End: */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/lance32.c linux/drivers/net/lance32.c --- v2.0.33/linux/drivers/net/lance32.c Mon Aug 4 12:11:05 1997 +++ linux/drivers/net/lance32.c Wed Dec 31 16:00:00 1969 @@ -1,849 +0,0 @@ -/* lance32.c: An AMD PCnet32 ethernet driver for linux. */ -/* - * Copyright 1996 Thomas Bogendoerfer - * - * Derived from the lance driver written 1993,1994,1995 by Donald Becker. - * - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. - * - * This software may be used and distributed according to the terms - * of the GNU Public License, incorporated herein by reference. - * - * This driver is for PCnet32 and PCnetPCI based ethercards - */ - -static const char *version = "lance32.c:v0.10 28.4.96 tsbogend@bigbug.franken.de\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#ifdef LANCE32_DEBUG -int lance32_debug = LANCE32_DEBUG; -#else -int lance32_debug = 1; -#endif - -/* - * Theory of Operation - * - * This driver uses the same software structure as the normal lance - * driver. So look for a verbose description in lance.c. The differences - * to the normal lance driver is the use of the 32bit mode of PCnet32 - * and PCnetPCI chips. Because these chips are 32bit chips, there is no - * 16MB limitation and we don't need bounce buffers. - */ - -/* - * History: - * v0.01: Initial version - * only tested on Alpha Noname Board - * v0.02: changed IRQ handling for new interrupt scheme (dev_id) - * tested on a ASUS SP3G - * v0.10: fixed a odd problem with the 79C794 in a Compaq Deskpro XL - * looks like the 974 doesn't like stopping and restarting in a - * short period of time; now we do a reinit of the lance; the - * bug was triggered by doing ifconfig eth0 broadcast - * and hangs the machine (thanks to Klaus Liedl for debugging) - */ - - -/* - * Set the number of Tx and Rx buffers, using Log_2(# buffers). - * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. - * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). - */ -#ifndef LANCE_LOG_TX_BUFFERS -#define LANCE_LOG_TX_BUFFERS 4 -#define LANCE_LOG_RX_BUFFERS 4 -#endif - -#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 12) - -#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 4) - -#define PKT_BUF_SZ 1544 - -/* Offsets from base I/O address. */ -#define LANCE_DATA 0x10 -#define LANCE_ADDR 0x12 -#define LANCE_RESET 0x14 -#define LANCE_BUS_IF 0x16 -#define LANCE_TOTAL_SIZE 0x18 - -/* The LANCE Rx and Tx ring descriptors. */ -struct lance32_rx_head { - u32 base; - s16 buf_length; - s16 status; - u32 msg_length; - u32 reserved; -}; - -struct lance32_tx_head { - u32 base; - s16 length; - s16 status; - u32 misc; - u32 reserved; -}; - - -/* The LANCE 32-Bit initialization block, described in databook. */ -struct lance32_init_block { - u16 mode; - u16 tlen_rlen; - u8 phys_addr[6]; - u16 reserved; - u32 filter[2]; - /* Receive and transmit ring base, along with extra bits. */ - u32 rx_ring; - u32 tx_ring; -}; - -struct lance32_private { - /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */ - struct lance32_rx_head rx_ring[RX_RING_SIZE]; - struct lance32_tx_head tx_ring[TX_RING_SIZE]; - struct lance32_init_block init_block; - const char *name; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ - int cur_rx, cur_tx; /* The next free ring entry */ - int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - int dma; - struct enet_statistics stats; - char tx_full; - unsigned long lock; -}; - -static int lance32_open(struct device *dev); -static void lance32_init_ring(struct device *dev); -static int lance32_start_xmit(struct sk_buff *skb, struct device *dev); -static int lance32_rx(struct device *dev); -static void lance32_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int lance32_close(struct device *dev); -static struct enet_statistics *lance32_get_stats(struct device *dev); -static void lance32_set_multicast_list(struct device *dev); - - - -/* lance32_probe1 */ -void lance32_probe1(struct device *dev, char *chipname, int pci_irq_line) -{ - struct lance32_private *lp; - int ioaddr = dev->base_addr; - short dma_channels; /* Mark spuriously-busy DMA channels */ - int i; - - /* Make certain the data structures used by the LANCE are 16byte aligned and DMAble. */ - lp = (struct lance32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15); - - memset(lp, 0, sizeof(*lp)); - dev->priv = lp; - lp->name = chipname; - lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL); - - lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */ - lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS; - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring); - lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring); - - /* switch lance to 32bit mode */ - outw(0x0014, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); - - outw(0x0001, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - outw(0x0000, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - - if (pci_irq_line) { - dev->dma = 4; /* Native bus-master, no DMA channel needed. */ - dev->irq = pci_irq_line; - } else { - /* The DMA channel may be passed in PARAM1. */ - if (dev->mem_start & 0x07) - dev->dma = dev->mem_start & 0x07; - } - - if (dev->dma == 0) { - /* Read the DMA channel status register, so that we can avoid - stuck DMA channels in the DMA detection below. */ - dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | - (inb(DMA2_STAT_REG) & 0xf0); - } - if (dev->irq >= 2) - printk(" assigned IRQ %d", dev->irq); - else { - /* To auto-IRQ we enable the initialization-done and DMA error - interrupts. For ISA boards we get a DMA error, but VLB and PCI - boards will work. */ - autoirq_setup(0); - - /* Trigger an initialization just for the interrupt. */ - outw(0x0041, ioaddr+LANCE_DATA); - - dev->irq = autoirq_report(1); - if (dev->irq) - printk(", probed IRQ %d", dev->irq); - else { - printk(", failed to detect IRQ line.\n"); - return; - } - - /* Check for the initialization done bit, 0x0100, which means - that we don't need a DMA channel. */ - if (inw(ioaddr+LANCE_DATA) & 0x0100) - dev->dma = 4; - } - - if (dev->dma == 4) { - printk(", no DMA needed.\n"); - } else if (dev->dma) { - if (request_dma(dev->dma, chipname)) { - printk("DMA %d allocation failed.\n", dev->dma); - return; - } else - printk(", assigned DMA %d.\n", dev->dma); - } else { /* OK, we have to auto-DMA. */ - for (i = 0; i < 4; i++) { - static const char dmas[] = { 5, 6, 7, 3 }; - int dma = dmas[i]; - int boguscnt; - - /* Don't enable a permanently busy DMA channel, or the machine - will hang. */ - if (test_bit(dma, &dma_channels)) - continue; - outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */ - if (request_dma(dma, chipname)) - continue; - set_dma_mode(dma, DMA_MODE_CASCADE); - enable_dma(dma); - - /* Trigger an initialization. */ - outw(0x0001, ioaddr+LANCE_DATA); - for (boguscnt = 100; boguscnt > 0; --boguscnt) - if (inw(ioaddr+LANCE_DATA) & 0x0900) - break; - if (inw(ioaddr+LANCE_DATA) & 0x0100) { - dev->dma = dma; - printk(", DMA %d.\n", dev->dma); - break; - } else { - disable_dma(dma); - free_dma(dma); - } - } - if (i == 4) { /* Failure: bail. */ - printk("DMA detection failed.\n"); - return; - } - } - - outw(0x0002, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); - - /* Reset the LANCE - this should prevent any more interrupts from arriving */ - inw(ioaddr+LANCE_RESET); - - if (lance32_debug > 0) - printk(version); - - /* The LANCE-specific entries in the device structure. */ - dev->open = &lance32_open; - dev->hard_start_xmit = &lance32_start_xmit; - dev->stop = &lance32_close; - dev->get_stats = &lance32_get_stats; - dev->set_multicast_list = &lance32_set_multicast_list; - - return; -} - - -static int -lance32_open(struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int ioaddr = dev->base_addr; - int i; - - if (dev->irq == 0 || - request_irq(dev->irq, &lance32_interrupt, 0, lp->name, (void *)dev)) { - return -EAGAIN; - } - - irq2dev_map[dev->irq] = dev; - - /* Reset the LANCE */ - inw(ioaddr+LANCE_RESET); - - /* switch lance to 32bit mode */ - outw(0x0014, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); - - /* The DMA controller is used as a no-operation slave, "cascade mode". */ - if (dev->dma != 4) { - enable_dma(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_CASCADE); - } - - /* Turn on auto-select of media (AUI, BNC). */ - outw(0x0002, ioaddr+LANCE_ADDR); - outw(0x0002, ioaddr+LANCE_BUS_IF); - - if (lance32_debug > 1) - printk("%s: lance32_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", - dev->name, dev->irq, dev->dma, - (u32) virt_to_bus(lp->tx_ring), - (u32) virt_to_bus(lp->rx_ring), - (u32) virt_to_bus(&lp->init_block)); - - lp->init_block.mode = 0x0000; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lance32_init_ring(dev); - - /* Re-initialize the LANCE, and start it when done. */ - outw(0x0001, ioaddr+LANCE_ADDR); - outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - - outw(0x0004, ioaddr+LANCE_ADDR); - outw(0x0915, ioaddr+LANCE_DATA); - - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0001, ioaddr+LANCE_DATA); - - dev->tbusy = 0; - dev->interrupt = 0; - dev->start = 1; - i = 0; - while (i++ < 100) - if (inw(ioaddr+LANCE_DATA) & 0x0100) - break; - /* - * We used to clear the InitDone bit, 0x0100, here but Mark Stockton - * reports that doing so triggers a bug in the '974. - */ - outw(0x0042, ioaddr+LANCE_DATA); - - if (lance32_debug > 2) - printk("%s: LANCE32 open after %d ticks, init block %#x csr0 %4.4x.\n", - dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA)); - - return 0; /* Always succeed */ -} - -/* - * The LANCE has been halted for one reason or another (busmaster memory - * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, - * etc.). Modern LANCE variants always reload their ring-buffer - * configuration when restarted, so we must reinitialize our ring - * context before restarting. As part of this reinitialization, - * find all packets still on the Tx ring and pretend that they had been - * sent (in effect, drop the packets on the floor) - the higher-level - * protocols will time out and retransmit. It'd be better to shuffle - * these skbs to a temp list and then actually re-Tx them after - * restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com - */ - -static void -lance32_purge_tx_ring(struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int i; - - for (i = 0; i < TX_RING_SIZE; i++) { - if (lp->tx_skbuff[i]) { - dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE); - lp->tx_skbuff[i] = NULL; - } - } -} - - -/* Initialize the LANCE Rx and Tx rings. */ -static void -lance32_init_ring(struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int i; - - lp->lock = 0, lp->tx_full = 0; - lp->cur_rx = lp->cur_tx = 0; - lp->dirty_rx = lp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ); - lp->rx_ring[i].buf_length = -PKT_BUF_SZ; - lp->rx_ring[i].status = 0x8000; - } - /* The Tx buffer address is filled in as needed, but we do need to clear - the upper ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - lp->tx_ring[i].base = 0; - lp->tx_ring[i].status = 0; - } - - lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS; - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring); - lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring); -} - -static void -lance32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit) -{ - int i; - int ioaddr = dev->base_addr; - - lance32_purge_tx_ring(dev); - lance32_init_ring(dev); - - outw(0x0000, ioaddr + LANCE_ADDR); - /* ReInit Ring */ - outw(0x0001, ioaddr + LANCE_DATA); - i = 0; - while (i++ < 100) - if (inw(ioaddr+LANCE_DATA) & 0x0100) - break; - - outw(csr0_bits, ioaddr + LANCE_DATA); -} - -static int -lance32_start_xmit(struct sk_buff *skb, struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int ioaddr = dev->base_addr; - int entry; - unsigned long flags; - - /* Transmitter timeout, serious problems. */ - if (dev->tbusy) { - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 60) /* It can take this long to run through the 16 retries */ - return 1; - outw(0, ioaddr+LANCE_ADDR); - printk("%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, inw(ioaddr+LANCE_DATA)); - outw(0x0004, ioaddr+LANCE_DATA); - lp->stats.tx_errors++; -#ifndef final_version - { - int i; - printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", - lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", - lp->cur_rx); - for (i = 0 ; i < RX_RING_SIZE; i++) - printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", - lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, - lp->rx_ring[i].msg_length); - for (i = 0 ; i < TX_RING_SIZE; i++) - printk("%s %08x %04x %04x %08x ", i & 0x1 ? "" : "\n ", - lp->tx_ring[i].base, -lp->tx_ring[i].length, - lp->tx_ring[i].status, - lp->tx_ring[i].misc); - printk("\n"); - } -#endif - lance32_restart(dev, 0x0042, 1); - - dev->tbusy=0; - dev->trans_start = jiffies; - dev_kfree_skb(skb, FREE_WRITE); - return 0; - } - - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - if (skb->len <= 0) - return 0; - - if (lance32_debug > 3) { - outw(0x0000, ioaddr+LANCE_ADDR); - printk("%s: lance32_start_xmit() called, csr0 %4.4x.\n", dev->name, - inw(ioaddr+LANCE_DATA)); - outw(0x0000, ioaddr+LANCE_DATA); - } - - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ - if (set_bit(0, (void*)&dev->tbusy) != 0) { - printk("%s: Transmitter access conflict.\n", dev->name); - return 1; - } - - if (set_bit(0, (void*)&lp->lock) != 0) { - if (lance32_debug > 0) - printk("%s: tx queue lock!.\n", dev->name); - /* don't clear dev->tbusy flag. */ - return 1; - } - - /* Fill in a Tx ring entry */ - - /* Mask to ring buffer boundary. */ - entry = lp->cur_tx & TX_RING_MOD_MASK; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - lp->tx_ring[entry].length = -skb->len; - - lp->tx_ring[entry].misc = 0x00000000; - - lp->tx_skbuff[entry] = skb; - lp->tx_ring[entry].base = (u32)virt_to_bus(skb->data); - lp->tx_ring[entry].status = 0x8300; - - lp->cur_tx++; - - /* Trigger an immediate send poll. */ - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0048, ioaddr+LANCE_DATA); - - dev->trans_start = jiffies; - - save_flags(flags); - cli(); - lp->lock = 0; - if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) - dev->tbusy=0; - else - lp->tx_full = 1; - restore_flags(flags); - - return 0; -} - -/* The LANCE32 interrupt handler. */ -static void -lance32_interrupt(int irq, void *dev_id, struct pt_regs * regs) -{ - struct device *dev = (struct device *)dev_id; - struct lance32_private *lp; - int csr0, ioaddr, boguscnt=10; - int must_restart; - - if (dev == NULL) { - printk ("lance32_interrupt(): irq %d for unknown device.\n", irq); - return; - } - - ioaddr = dev->base_addr; - lp = (struct lance32_private *)dev->priv; - if (dev->interrupt) - printk("%s: Re-entering the interrupt handler.\n", dev->name); - - dev->interrupt = 1; - - outw(0x00, dev->base_addr + LANCE_ADDR); - while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 - && --boguscnt >= 0) { - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); - - must_restart = 0; - - if (lance32_debug > 5) - printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", - dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); - - if (csr0 & 0x0400) /* Rx interrupt */ - lance32_rx(dev); - - if (csr0 & 0x0200) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; - - while (dirty_tx < lp->cur_tx) { - int entry = dirty_tx & TX_RING_MOD_MASK; - int status = lp->tx_ring[entry].status; - - if (status < 0) - break; /* It still hasn't been Txed */ - - lp->tx_ring[entry].base = 0; - - if (status & 0x4000) { - /* There was an major error, log it. */ - int err_status = lp->tx_ring[entry].misc; - lp->stats.tx_errors++; - if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; - if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; - if (err_status & 0x10000000) lp->stats.tx_window_errors++; - if (err_status & 0x40000000) { - /* Ackk! On FIFO errors the Tx unit is turned off! */ - lp->stats.tx_fifo_errors++; - /* Remove this verbosity later! */ - printk("%s: Tx FIFO error! Status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - } else { - if (status & 0x1800) - lp->stats.collisions++; - lp->stats.tx_packets++; - } - - /* We must free the original skb */ - if (lp->tx_skbuff[entry]) { - dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE); - lp->tx_skbuff[entry] = 0; - } - dirty_tx++; - } - -#ifndef final_version - if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dirty_tx, lp->cur_tx, lp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (dev->tbusy) - dev->trans_start = jiffies; /* We are just starting the next transmit */ - - if (lp->tx_full && dev->tbusy - && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { - /* The ring is no longer full, clear tbusy. */ - lp->tx_full = 0; - dev->tbusy = 0; - mark_bh(NET_BH); - } - - lp->dirty_tx = dirty_tx; - } - - /* Log misc errors. */ - if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */ - if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */ - if (csr0 & 0x0800) { - printk("%s: Bus master arbitration failure, status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - - if (must_restart) { - /* stop the chip to clear the error condition, then restart */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x0004, dev->base_addr + LANCE_DATA); - lance32_restart(dev, 0x0002, 0); - } - } - - /* Clear any other interrupt, and set interrupt enable. */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x7940, dev->base_addr + LANCE_DATA); - - if (lance32_debug > 4) - printk("%s: exiting interrupt, csr%d=%#4.4x.\n", - dev->name, inw(ioaddr + LANCE_ADDR), - inw(dev->base_addr + LANCE_DATA)); - - dev->interrupt = 0; - return; -} - -static int -lance32_rx(struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int entry = lp->cur_rx & RX_RING_MOD_MASK; - int i; - - /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].status >= 0) { - int status = lp->rx_ring[entry].status >> 8; - - if (status != 0x03) { /* There was an error. */ - /* There is a tricky error noted by John Murphy, - to Russ Nelson: Even with full-sized - buffers it's possible for a jabber packet to use two - buffers, with only the last correctly noting the error. */ - if (status & 0x01) /* Only count a general error at the */ - lp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x20) lp->stats.rx_frame_errors++; - if (status & 0x10) lp->stats.rx_over_errors++; - if (status & 0x08) lp->stats.rx_crc_errors++; - if (status & 0x04) lp->stats.rx_fifo_errors++; - lp->rx_ring[entry].status &= 0x03ff; - } - else - { - /* Malloc up new buffer, compatible with net-2e. */ - short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4; - struct sk_buff *skb; - - if(pkt_len<60) - { - printk("%s: Runt packet!\n",dev->name); - lp->stats.rx_errors++; - } - else - { - skb = dev_alloc_skb(pkt_len+2); - if (skb == NULL) - { - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - for (i=0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status < 0) - break; - - if (i > RX_RING_SIZE -2) - { - lp->stats.rx_dropped++; - lp->rx_ring[entry].status |= 0x8000; - lp->cur_rx++; - } - break; - } - skb->dev = dev; - skb_reserve(skb,2); /* 16 byte align */ - skb_put(skb,pkt_len); /* Make room */ - eth_copy_and_sum(skb, - (unsigned char *)bus_to_virt(lp->rx_ring[entry].base), - pkt_len,0); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - lp->stats.rx_packets++; - } - } - /* The docs say that the buffer length isn't touched, but Andrew Boyd - of QNX reports that some revs of the 79C965 clear it. */ - lp->rx_ring[entry].buf_length = -PKT_BUF_SZ; - lp->rx_ring[entry].status |= 0x8000; - entry = (++lp->cur_rx) & RX_RING_MOD_MASK; - } - - /* We should check that at least two ring entries are free. If not, - we should free one and mark stats->rx_dropped++. */ - - return 0; -} - -static int -lance32_close(struct device *dev) -{ - int ioaddr = dev->base_addr; - struct lance32_private *lp = (struct lance32_private *)dev->priv; - - dev->start = 0; - dev->tbusy = 1; - - outw(112, ioaddr+LANCE_ADDR); - lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - - outw(0, ioaddr+LANCE_ADDR); - - if (lance32_debug > 1) - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inw(ioaddr+LANCE_DATA)); - - /* We stop the LANCE here -- it occasionally polls - memory if we don't. */ - outw(0x0004, ioaddr+LANCE_DATA); - - if (dev->dma != 4) - disable_dma(dev->dma); - - free_irq(dev->irq, dev); - - irq2dev_map[dev->irq] = 0; - - return 0; -} - -static struct enet_statistics * -lance32_get_stats(struct device *dev) -{ - struct lance32_private *lp = (struct lance32_private *)dev->priv; - int ioaddr = dev->base_addr; - unsigned short saved_addr; - unsigned long flags; - - save_flags(flags); - cli(); - saved_addr = inw(ioaddr+LANCE_ADDR); - outw(112, ioaddr+LANCE_ADDR); - lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - outw(saved_addr, ioaddr+LANCE_ADDR); - restore_flags(flags); - - return &lp->stats; -} - -/* Set or clear the multicast filter for this adaptor. - */ - -static void lance32_set_multicast_list(struct device *dev) -{ - int ioaddr = dev->base_addr; - struct lance32_private *lp = (struct lance32_private *)dev->priv; - - if (dev->flags&IFF_PROMISC) { - /* Log any net taps. */ - printk("%s: Promiscuous mode enabled.\n", dev->name); - lp->init_block.mode = 0x8000; - } else { - int num_addrs=dev->mc_count; - if(dev->flags&IFF_ALLMULTI) - num_addrs=1; - /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ - memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter)); - lp->init_block.mode = 0x0000; - } - - outw(0, ioaddr+LANCE_ADDR); - outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */ - - lance32_restart(dev, 0x0042, 0); /* Resume normal operation */ - -} - - -/* - * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance32.c" - * c-indent-level: 4 - * tab-width: 4 - * End: - */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/ne.c linux/drivers/net/ne.c --- v2.0.33/linux/drivers/net/ne.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/net/ne.c Wed Jun 3 15:17:47 1998 @@ -118,7 +118,9 @@ static unsigned char pci_irq_line = 0; int ne_probe(struct device *dev); +#ifdef CONFIG_PCI static int ne_probe_pci(struct device *dev); +#endif static int ne_probe1(struct device *dev, int ioaddr); static int ne_open(struct device *dev); @@ -205,6 +207,7 @@ for (i = 0; pci_clone_list[i].vendor != 0; i++) { unsigned char pci_bus, pci_device_fn; unsigned int pci_ioaddr; + u16 pci_command, new_command; int pci_index; for (pci_index = 0; pci_index < 8; pci_index++) { @@ -226,6 +229,20 @@ if (pci_irq_line == 0) continue; /* Try next PCI ID */ printk("ne.c: PCI BIOS reports NE 2000 clone at i/o %#x, irq %d.\n", pci_ioaddr, pci_irq_line); + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + + /* Activate the card: fix for brain-damaged Win98 BIOSes. */ + new_command = pci_command | PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " NE2k clone! 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); + } + if (ne_probe1(dev, pci_ioaddr) != 0) { /* Shouldn't happen. */ printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr); pci_irq_line = 0; @@ -420,9 +437,10 @@ } /* Snarf the interrupt now. There's no point in waiting since we cannot - share and the board will usually be enabled. */ + share (with ISA cards) and the board will usually be enabled. */ { - int irqval = request_irq(dev->irq, ei_interrupt, 0, name, NULL); + int irqval = request_irq(dev->irq, ei_interrupt, + pci_irq_line ? SA_SHIRQ : 0, name, dev); if (irqval) { printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); return EAGAIN; @@ -730,6 +748,7 @@ static int io[MAX_NE_CARDS] = { 0, }; static int irq[MAX_NE_CARDS] = { 0, }; +static int bad[MAX_NE_CARDS] = { 0, }; /* This is set up so that no autoprobe takes place. We can't guarantee that the ne2k probe is the last 8390 based probe to take place (as it @@ -747,6 +766,7 @@ dev->irq = irq[this_dev]; dev->base_addr = io[this_dev]; dev->init = ne_probe; + dev->mem_end = bad[this_dev]; if (register_netdev(dev) == 0) { found++; continue; @@ -773,7 +793,7 @@ if (dev->priv != NULL) { kfree(dev->priv); dev->priv = NULL; - free_irq(dev->irq, NULL); + free_irq(dev->irq, dev); irq2dev_map[dev->irq] = NULL; release_region(dev->base_addr, NE_IO_EXTENT); unregister_netdev(dev); diff -u --recursive --new-file v2.0.33/linux/drivers/net/ne2k-pci.c linux/drivers/net/ne2k-pci.c --- v2.0.33/linux/drivers/net/ne2k-pci.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/ne2k-pci.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,641 @@ +/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */ +/* + A Linux device driver for PCI NE2000 clones. + + Authorship and other copyrights: + 1992-1998 by Donald Becker, NE2000 core and various modifications. + 1995-1998 by Paul Gortmaker, core modifications and PCI support. + + Copyright 1993 assigned to the United States Government as represented + by the Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + People are making PCI ne2000 clones! Oh the horror, the horror... + + Issues remaining: + No full-duplex support. +*/ + +/* Our copyright info must remain in the binary. */ +static const char *version = +"ne2k-pci.c:v0.99L 2/7/98 D. Becker/P. Gortmaker http://cesdis.gsfc.nasa.gov/linux/drivers/ne2k-pci.html\n"; + +#ifdef MODVERSIONS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "8390.h" + +/* Set statically or when loading the driver module. */ +static debug = 1; + +/* Some defines that people can play with if so inclined. */ + +/* Do #define LOAD_8390_BY_KERNELD to automatically load 8390 support. */ +#ifdef LOAD_8390_BY_KERNELD +#include +#endif +/* Use 32 bit data-movement operations instead of 16 bit. */ +#define USE_LONGIO + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + +static struct { + unsigned short vendor, dev_id; + char *name; +} +pci_clone_list[] = { + {0x10ec, 0x8029, "RealTek RTL-8029"}, + {0x1050, 0x0940, "Winbond 89C940"}, + {0x11f6, 0x1401, "Compex RL2000"}, + {0x8e2e, 0x3000, "KTI ET32P2"}, + {0x4a14, 0x5000, "NetVin NV5000SC"}, + {0x1106, 0x0926, "Via 82C926"}, + {0,} +}; + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT 0x20 + +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + +int ne2k_pci_probe(struct device *dev); +static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq); + +static int ne_open(struct device *dev); +static int ne_close(struct device *dev); + +static void ne_reset_8390(struct device *dev); +static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne_block_input(struct device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne_block_output(struct device *dev, const int count, + const unsigned char *buf, const int start_page); + + + +/* No room in the standard 8390 structure for extra info we need. */ +struct ne2k_pci_card { + struct ne2k_pci_card *next; + struct device *dev; + unsigned char pci_bus, pci_device_fn; +}; +/* A list of all installed devices, for removing the driver module. */ +static struct ne2k_pci_card *ne2k_card_list = NULL; + +#ifdef LOAD_8390_BY_KERNELD +static int (*Lethdev_init)(struct device *dev); +static void (*LNS8390_init)(struct device *dev, int startp); +static int (*Lei_open)(struct device *dev); +static int (*Lei_close)(struct device *dev); +static void (*Lei_interrupt)(int irq, void *dev_id, struct pt_regs *regs); +#else +#define Lethdev_init ethdev_init +#define LNS8390_init NS8390_init +#define Lei_open ei_open +#define Lei_close ei_close +#define Lei_interrupt ei_interrupt +#endif + +#ifdef MODULE + +int +init_module(void) +{ + /* We must emit version information. */ + if (debug) + printk(KERN_INFO "%s", version); + + return ne2k_pci_probe(0); +} + +void +cleanup_module(void) +{ + struct device *dev; + struct ne2k_pci_card *this_card; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (ne2k_card_list) { + dev = ne2k_card_list->dev; + unregister_netdev(dev); + release_region(dev->base_addr, NE_IO_EXTENT); + kfree(dev); + this_card = ne2k_card_list; + ne2k_card_list = ne2k_card_list->next; + kfree(this_card); + } + +#ifdef LOAD_8390_BY_KERNELD + release_module("8390", 0); +#endif +} + +#endif /* MODULE */ + +/* + NEx000-clone boards have a Station Address (SA) PROM (SAPROM) in the packet + buffer memory space. By-the-spec NE2000 clones have 0x57,0x57 in bytes + 0x0e,0x0f of the SAPROM, while other supposed NE2000 clones must be + detected by their SA prefix. + + Reading the SAPROM from a word-wide card with the 8390 set in byte-wide + mode results in doubled values, which can be detected and compensated for. + + The probe is also responsible for initializing the card and filling + in the 'dev' and 'ei_status' structures. +*/ + +#ifdef HAVE_DEVLIST +struct netdev_entry netcard_drv = +{"ne2k_pci", ne2k_pci_probe1, NE_IO_EXTENT, 0}; +#endif + +int ne2k_pci_probe(struct device *dev) +{ + static int pci_index = 0; /* Static, for multiple calls. */ + int cards_found = 0; + int i; + + if ( ! pcibios_present()) + return -ENODEV; + +#ifndef MODULE + { + static unsigned version_printed = 0; + if (version_printed++ == 0) + printk(KERN_INFO "%s", version); + } +#endif + + for (;pci_index < 0xff; pci_index++) { + unsigned char pci_bus, pci_device_fn; + u8 pci_irq_line; + u16 pci_command, new_command, vendor, device; + u32 pci_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); + + /* Note: some vendor IDs (RealTek) have non-NE2k cards as well. */ + for (i = 0; pci_clone_list[i].vendor != 0; i++) + if (pci_clone_list[i].vendor == vendor + && pci_clone_list[i].dev_id == device) + break; + if (pci_clone_list[i].vendor == 0) + continue; + + 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); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + + /* Avoid already found cards from previous calls */ + if (check_region(pci_ioaddr, NE_IO_EXTENT)) + continue; + + /* Activate the card: fix for brain-damaged Win98 BIOSes. */ + new_command = pci_command | PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " NE2k clone! 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); + } + + if (pci_irq_line <= 0 || pci_irq_line >= NR_IRQS) + printk(KERN_WARNING " WARNING: The PCI BIOS assigned this PCI NE2k" + " card to IRQ %d, which is unlikely to work!.\n" + KERN_WARNING " You should use the PCI BIOS setup to assign" + " a valid IRQ line.\n", pci_irq_line); + + printk("ne2k-pci.c: PCI NE2000 clone '%s' at I/O %#x, IRQ %d.\n", + pci_clone_list[i].name, pci_ioaddr, pci_irq_line); + dev = ne2k_pci_probe1(dev, pci_ioaddr, pci_irq_line); + if (dev == 0) { + /* Should not happen. */ + printk(KERN_ERR "ne2k-pci: Probe of PCI card at %#x failed.\n", + pci_ioaddr); + continue; + } else { + struct ne2k_pci_card *ne2k_card = + kmalloc(sizeof(struct ne2k_pci_card), GFP_KERNEL); + ne2k_card->next = ne2k_card_list; + ne2k_card_list = ne2k_card; + ne2k_card->dev = dev; + ne2k_card->pci_bus = pci_bus; + ne2k_card->pci_device_fn = pci_device_fn; + } + dev = 0; + + cards_found++; + } + + return cards_found ? 0 : -ENODEV; +} + +static struct device *ne2k_pci_probe1(struct device *dev, int ioaddr, int irq) +{ + int i; + unsigned char SA_prom[32]; + const char *name = NULL; + int start_page, stop_page; + int reg0 = inb(ioaddr); + + if (reg0 == 0xFF) + return 0; + + /* Do a preliminary verification that we have a 8390. */ + { + int regd; + outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb(ioaddr + 0x0d); + outb(0xff, ioaddr + 0x0d); + outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb(ioaddr + EN0_COUNTER0) != 0) { + outb(reg0, ioaddr); + outb(regd, ioaddr + 0x0d); /* Restore the old values. */ + return 0; + } + } + + dev = init_etherdev(dev, 0); + + /* Reset card. Who knows what dain-bramaged state it was left in. */ + { + unsigned long reset_start_time = jiffies; + + outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + + /* This looks like a horrible timing loop, but it should never take + more than a few cycles. + */ + while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) + /* Limit wait: '2' avoids jiffy roll-over. */ + if (jiffies - reset_start_time > 2) { + printk("ne2k-pci: Card failure (no reset ack).\n"); + return 0; + } + + outb(0xff, ioaddr + EN0_ISR); /* Ack all intr. */ + } + +#if defined(LOAD_8390_BY_KERNELD) + /* We are now certain the 8390 module is required. */ + if (request_module("8390")) { + printk("ne2k-pci: Failed to load the 8390 core module.\n"); + return 0; + } + if ((Lethdev_init = (void*)get_module_symbol(0, "ethdev_init")) == 0 || + (LNS8390_init = (void*)get_module_symbol(0, "NS8390_init")) == 0 || + (Lei_open = (void*)get_module_symbol(0, "ei_open")) == 0 || + (Lei_close = (void*)get_module_symbol(0, "ei_close")) == 0 || + (Lei_interrupt = (void*)get_module_symbol(0, "ei_interrupt")) == 0 ) { + printk("ne2k-pci: Failed to resolve an 8390 symbol.\n"); + release_module("8390", 0); + return 0; + } +#endif + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, 0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x49, EN0_DCFG}, /* Set word-wide access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) + outb(program_seq[i].value, ioaddr + program_seq[i].offset); + + } + +#ifdef notdef + /* Some broken PCI cards don't respect the byte-wide + request in program_seq above, and hence don't have doubled up values. + */ + for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { + SA_prom[i] = inb(ioaddr + NE_DATAPORT); + SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); + if (SA_prom[i] != SA_prom[i+1]) + sa_prom_doubled = 0; + } + + if (sa_prom_doubled) + for (i = 0; i < 16; i++) + SA_prom[i] = SA_prom[i+i]; +#else + for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++) + SA_prom[i] = inb(ioaddr + NE_DATAPORT); + +#endif + + /* We always set the 8390 registers for word mode. */ + outb(0x49, ioaddr + EN0_DCFG); + start_page = NESM_START_PG; + stop_page = NESM_STOP_PG; + + /* Set up the rest of the parameters. */ + name = "PCI NE2000"; + + dev->irq = irq; + dev->base_addr = ioaddr; + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (Lethdev_init(dev)) { + printk ("%s: unable to get memory for dev->priv.\n", dev->name); + return 0; + } + + request_region(ioaddr, NE_IO_EXTENT, dev->name); + + printk("%s: %s found at %#x, IRQ %d, ", + dev->name, name, ioaddr, dev->irq); + for(i = 0; i < 6; i++) { + printk("%2.2X%s", SA_prom[i], i == 5 ? ".\n": ":"); + dev->dev_addr[i] = SA_prom[i]; + } + + ei_status.name = name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; + ei_status.word16 = 1; + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne_reset_8390; + ei_status.block_input = &ne_block_input; + ei_status.block_output = &ne_block_output; + ei_status.get_8390_hdr = &ne_get_8390_hdr; + dev->open = &ne_open; + dev->stop = &ne_close; + LNS8390_init(dev, 0); + return dev; +} + +static int +ne_open(struct device *dev) +{ + if (request_irq(dev->irq, Lei_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + Lei_open(dev); + MOD_INC_USE_COUNT; + return 0; +} + +static int +ne_close(struct device *dev) +{ + Lei_close(dev); + free_irq(dev->irq, dev); + MOD_DEC_USE_COUNT; + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ +static void +ne_reset_8390(struct device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (debug > 1) printk("%s: Resetting the 8390 t=%ld...", + dev->name, jiffies); + + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2) { + printk("%s: ne_reset_8390() did not complete.\n", dev->name); + break; + } + outb(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void +ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + + int nic_base = dev->base_addr; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_get_8390_hdr " + "[DMAstat:%d][irqlock:%d][intr:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + + ei_status.dmaing |= 0x01; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb(0, nic_base + EN0_RCNTHI); + outb(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb(ring_page, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + +#if defined(USE_LONGIO) + *(u32*)hdr = inl(NE_BASE + NE_DATAPORT); +#else + insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); +#endif + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void +ne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + int nic_base = dev->base_addr; + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_input " + "[DMAstat:%d][irqlock:%d][intr:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb(ring_offset >> 8, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + +#if defined(USE_LONGIO) + insl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) + *((u16*)buf)++ = inw(NE_BASE + NE_DATAPORT); + if (count & 1) + *buf = inb(NE_BASE + NE_DATAPORT); + } +#else + insw(NE_BASE + NE_DATAPORT,buf,count>>1); + if (count & 0x01) { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); + } +#endif + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void +ne_block_output(struct device *dev, int count, + const unsigned char *buf, const int start_page) +{ + int nic_base = NE_BASE; + unsigned long dma_start; + + /* On little-endian it's always safe to round the count up for + word writes. */ + if (count & 0x01) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_output." + "[DMAstat:%d][irqlock:%d][intr:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock, + dev->interrupt); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + outb(0x42, nic_base + EN0_RCNTLO); + outb(0x00, nic_base + EN0_RCNTHI); + outb(0x42, nic_base + EN0_RSARLO); + outb(0x00, nic_base + EN0_RSARHI); + outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); +#endif + outb(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb(count & 0xff, nic_base + EN0_RCNTLO); + outb(count >> 8, nic_base + EN0_RCNTHI); + outb(0x00, nic_base + EN0_RSARLO); + outb(start_page, nic_base + EN0_RSARHI); + outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); +#if defined(USE_LONGIO) + outsl(NE_BASE + NE_DATAPORT, buf, count>>2); + if (count & 3) { + buf += count & ~3; + if (count & 2) + outw(*((u16*)buf)++, NE_BASE + NE_DATAPORT); + } +#else + outsw(NE_BASE + NE_DATAPORT, buf, count>>1); +#endif + + dma_start = jiffies; + + while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2) { /* Avoid clock roll-over. */ + printk("%s: timeout waiting for Tx RDC.\n", dev->name); + ne_reset_8390(dev); + LNS8390_init(dev,1); + break; + } + + outb(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + + +/* + * Local variables: + * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c" + * alt-compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/ -c ne2k-pci.c" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * version-control: t + * kept-new-versions: 5 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c --- v2.0.33/linux/drivers/net/new_tunnel.c Tue Aug 12 13:21:12 1997 +++ linux/drivers/net/new_tunnel.c Wed Jun 3 15:17:47 1998 @@ -267,7 +267,7 @@ /* Tack on our header */ new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen); - new_skb->mac.raw = new_skb->ip_hdr; + new_skb->mac.raw = (void *)new_skb->ip_hdr; /* Free the old packet, we no longer need it */ dev_kfree_skb(skb, FREE_WRITE); diff -u --recursive --new-file v2.0.33/linux/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c --- v2.0.33/linux/drivers/net/pcnet32.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/pcnet32.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,956 @@ +/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */ +/* + * Copyright 1996,97 Thomas Bogendoerfer, 1993-1995,1998 Donald Becker + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency. + * + * Derived from the lance driver written 1993-1995 by Donald Becker. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * This driver is for AMD PCnet-PCI based ethercards + */ + +static const char *version = "pcnet32.c:v0.99B 4/4/98 DJBecker/TSBogend.\n"; + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. + * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). + */ +#define PCNET_LOG_TX_BUFFERS 4 +#define PCNET_LOG_RX_BUFFERS 4 + +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Driver verbosity level. 0 = no messages, 7 = wordy death. + Modify here, or when loading as a module. */ +static int pcnet32_debug = 1; + +/* + * Theory of Operation + * + * This driver uses the same software structure as the normal lance + * driver. So look for a verbose description in lance.c. The differences + * to the normal lance driver is the use of the 32bit mode of PCnet32 + * and PCnetPCI chips. Because these chips are 32bit chips, there is no + * 16MB limitation and we don't need bounce buffers. + */ + +/* + * History: + * v0.01: Initial version + * only tested on Alpha Noname Board + * v0.02: changed IRQ handling for new interrupt scheme (dev_id) + * tested on a ASUS SP3G + * v0.10: fixed an odd problem with the 79C794 in a Compaq Deskpro XL + * looks like the 974 doesn't like stopping and restarting in a + * short period of time; now we do a reinit of the lance; the + * bug was triggered by doing ifconfig eth0 broadcast + * and hangs the machine (thanks to Klaus Liedl for debugging) + * v0.12: by suggestion from Donald Becker: Renamed driver to pcnet32, + * made it standalone (no need for lance.c) + * v0.13: added additional PCI detecting for special PCI devices (Compaq) + * v0.14: stripped down additional PCI probe (thanks to David C Niemi + * and sveneric@xs4all.nl for testing this on their Compaq boxes) + * v0.15: added 79C965 (VLB) probe + * added interrupt sharing for PCI chips + * v0.16: fixed set_multicast_list on Alpha machines + * v0.17: removed hack from dev.c; now pcnet32 uses ethif_probe in Space.c + * v0.19: changed setting of autoselect bit + * v0.20: removed additional Compaq PCI probe; there is now a working one + * in arch/i386/bios32.c + * v0.21: added endian conversion for ppc, from work by cort@cs.nmt.edu + * v0.22: added printing of status to ring dump + * v0.23: changed enet_statistics to net_devive_stats + * v0.99: Changes for 2.0.34 final release. -djb + */ + + +#ifndef __powerpc__ +#define le16_to_cpu(val) (val) +#define le32_to_cpu(val) (val) +#endif +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +#define TX_RING_SIZE (1 << (PCNET_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((PCNET_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE (1 << (PCNET_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((PCNET_LOG_RX_BUFFERS) << 4) + +#define PKT_BUF_SZ 1544 + +/* Offsets from base I/O address. */ +enum pcnet_offsets { PCNET32_DATA=0x10, PCNET32_ADDR=0x12, PCNET32_RESET=0x14, + PCNET32_BUS_IF=0x16,}; +#define PCNET32_TOTAL_SIZE 0x20 + +/* The PCNET32 Rx and Tx ring descriptors. */ +struct pcnet32_rx_head { + u32 base; + s16 buf_length; + s16 status; + u32 msg_length; + u32 reserved; +}; + +struct pcnet32_tx_head { + u32 base; + s16 length; + s16 status; + u32 misc; + u32 reserved; +}; + +/* The PCNET32 32-Bit initialization block, described in databook. */ +struct pcnet32_init_block { + u16 mode; + u16 tlen_rlen; + u8 phys_addr[6]; + u16 reserved; + u32 filter[2]; + /* Receive and transmit ring base, along with extra bits. */ + u32 rx_ring; + u32 tx_ring; +}; + +struct pcnet32_private { + /* The Tx and Rx ring entries must be aligned on 16-byte boundaries + in 32bit mode. */ + struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; + struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; + struct pcnet32_init_block init_block; + const char *name; + struct device *next_module; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ + int cur_rx, cur_tx; /* The next free ring entry */ + int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + struct enet_statistics stats; + char tx_full; + unsigned long lock; +}; + +static struct pcnet_chip_type { + int id_number; + const char *name; + int flags; +} chip_table[] = { + {0x2420, "PCnet/PCI 79C970", 0}, + {0x2430, "PCnet32", 0}, + {0x2621, "PCnet/PCI II 79C970A", 0}, + {0x2623, "PCnet/PCI II 79C971A", 0}, + {0x0, "PCnet32 (unknown)", 0}, +}; + +/* Index of functions. */ +int pcnet32_probe(struct device *dev); +static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line); +static int pcnet32_open(struct device *dev); +static void pcnet32_init_ring(struct device *dev); +static int pcnet32_start_xmit(struct sk_buff *skb, struct device *dev); +static int pcnet32_rx(struct device *dev); +static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int pcnet32_close(struct device *dev); +static struct enet_statistics *pcnet32_get_stats(struct device *dev); +static void pcnet32_set_multicast_list(struct device *dev); + + +/* A list of all installed PCnet32 devices, for removing the driver module. */ +static struct device *root_pcnet32_dev = NULL; + +int pcnet32_probe (struct device *dev) +{ + static int pci_index = 0; /* Static, for multiple probe calls. */ + int cards_found = 0; + + if ( ! pcibios_present()) + return ENODEV; + + for (;pci_index < 0xff; pci_index++) { + u8 irq_line; + u16 pci_command, new_command; + unsigned char pci_bus, pci_device_fn; + u32 pci_ioaddr; + + if (pcibios_find_device (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, + pci_index, &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; + + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &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; + + /* Avoid already found cards from previous pcnet32_probe() calls */ + if (check_region(pci_ioaddr, PCNET32_TOTAL_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_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled the AMD Ethernet" + " device at %2x-%2x." + " 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 __powerpc__ + /* This is bogus! -djb */ + irq_line = 15; +#endif + + if (pcnet32_probe1(dev, pci_ioaddr, irq_line) != 0) { + /* Should never happen. */ + printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", + pci_ioaddr); + } else + dev = 0; + cards_found++; + } + + return cards_found ? 0 : -ENODEV; +} + + +/* pcnet32_probe1 */ +static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line) +{ + struct pcnet32_private *lp; + int i; + const char *chipname; + + /* check if there is really a pcnet chip on that ioaddr */ + if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57)) + return ENODEV; + + inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */ + + outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */ + if (inw(ioaddr+PCNET32_DATA) != 0x0004) + return ENODEV; + + /* Get the version of the chip. */ + outw(88, ioaddr+PCNET32_ADDR); + if (inw(ioaddr+PCNET32_ADDR) != 88) { + /* should never happen */ + return ENODEV; + } else { /* Good, it's a newer chip. */ + int chip_version = inw(ioaddr+PCNET32_DATA); + outw(89, ioaddr+PCNET32_ADDR); + chip_version |= inw(ioaddr+PCNET32_DATA) << 16; + if (pcnet32_debug > 2) + printk(" PCnet chip version is %#x.\n", chip_version); + if ((chip_version & 0xfff) != 0x003) + return ENODEV; + chip_version = (chip_version >> 12) & 0xffff; + for (i = 0; chip_table[i].id_number; i++) + if (chip_table[i].id_number == chip_version) + break; + chipname = chip_table[i].name; + } + + dev = init_etherdev(dev, 0); + + printk("%s: %s at %#3x,", dev->name, chipname, ioaddr); + + /* There is a 16 byte station address PROM at the base address. + The first six bytes are the station address. */ + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + + dev->base_addr = ioaddr; + request_region(ioaddr, PCNET32_TOTAL_SIZE, dev->name); + + /* Data structures used by the PCnet32 are 16byte aligned and DMAble. */ + lp = (struct pcnet32_private *) + (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15); + + memset(lp, 0, sizeof(*lp)); + dev->priv = lp; + + lp->next_module = root_pcnet32_dev; + root_pcnet32_dev = dev; + + lp->name = chipname; + lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL); + + lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ + lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring)); + lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring)); + + /* switch pcnet32 to 32bit mode */ + outw(0x0014, ioaddr+PCNET32_ADDR); + outw(0x0002, ioaddr+PCNET32_BUS_IF); + + outw(0x0001, ioaddr+PCNET32_ADDR); + inw(ioaddr+PCNET32_ADDR); + outw(virt_to_bus(&lp->init_block) & 0xffff, ioaddr+PCNET32_DATA); + outw(0x0002, ioaddr+PCNET32_ADDR); + inw(ioaddr+PCNET32_ADDR); + outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA); + outw(0x0000, ioaddr+PCNET32_ADDR); + inw(ioaddr+PCNET32_ADDR); + + dev->irq = irq_line; +#ifdef __powerpc__ + /* This is sooo bogus! -djb */ + irq_line = 15; +#endif + + outw(0x0002, ioaddr+PCNET32_ADDR); + /* only touch autoselect bit */ + outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF); + + if (pcnet32_debug > 0) + printk(version); + + /* The PCNET32-specific entries in the device structure. */ + dev->open = &pcnet32_open; + dev->hard_start_xmit = &pcnet32_start_xmit; + dev->stop = &pcnet32_close; + dev->get_stats = &pcnet32_get_stats; + dev->set_multicast_list = &pcnet32_set_multicast_list; + + /* Fill in the generic fields of the device structure. */ + ether_setup(dev); + return 0; +} + + +static int +pcnet32_open(struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + unsigned int ioaddr = dev->base_addr; + int i; + + if (dev->irq == 0 || + request_irq(dev->irq, &pcnet32_interrupt, SA_SHIRQ, + dev->name, (void *)dev)) { + return -EAGAIN; + } + MOD_INC_USE_COUNT; + + /* Reset the PCNET32 */ + inw(ioaddr+PCNET32_RESET); + + /* switch pcnet32 to 32bit mode */ + outw(0x0014, ioaddr+PCNET32_ADDR); + outw(0x0002, ioaddr+PCNET32_BUS_IF); + + /* Turn on auto-select of media (AUI, BNC). */ + outw(0x0002, ioaddr+PCNET32_ADDR); + /* only touch autoselect bit */ + outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF); + + if (pcnet32_debug > 1) + printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n", + dev->name, dev->irq, + (u32) virt_to_bus(lp->tx_ring), + (u32) virt_to_bus(lp->rx_ring), + (u32) virt_to_bus(&lp->init_block)); + + lp->init_block.mode = 0x0000; + lp->init_block.filter[0] = 0x00000000; + lp->init_block.filter[1] = 0x00000000; + pcnet32_init_ring(dev); + + /* Re-initialize the PCNET32, and start it when done. */ + outw(0x0001, ioaddr+PCNET32_ADDR); + outw(virt_to_bus(&lp->init_block) &0xffff, ioaddr+PCNET32_DATA); + outw(0x0002, ioaddr+PCNET32_ADDR); + outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA); + + outw(0x0004, ioaddr+PCNET32_ADDR); + outw(0x0915, ioaddr+PCNET32_DATA); + + outw(0x0000, ioaddr+PCNET32_ADDR); + outw(0x0001, ioaddr+PCNET32_DATA); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + i = 0; + while (i++ < 100) + if (inw(ioaddr+PCNET32_DATA) & 0x0100) + break; + /* + * We used to clear the InitDone bit, 0x0100, here but Mark Stockton + * reports that doing so triggers a bug in the '974. + */ + outw(0x0042, ioaddr+PCNET32_DATA); + + if (pcnet32_debug > 2) + printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n", + dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA)); + + return 0; /* Always succeed */ +} + +/* + * The LANCE has been halted for one reason or another (busmaster memory + * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, + * etc.). Modern LANCE variants always reload their ring-buffer + * configuration when restarted, so we must reinitialize our ring + * context before restarting. As part of this reinitialization, + * find all packets still on the Tx ring and pretend that they had been + * sent (in effect, drop the packets on the floor) - the higher-level + * protocols will time out and retransmit. It'd be better to shuffle + * these skbs to a temp list and then actually re-Tx them after + * restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com + */ + +static void +pcnet32_purge_tx_ring(struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + int i; + + for (i = 0; i < TX_RING_SIZE; i++) { + if (lp->tx_skbuff[i]) { + dev_kfree_skb(lp->tx_skbuff[i], FREE_WRITE); + lp->tx_skbuff[i] = NULL; + } + } +} + + +/* Initialize the PCNET32 Rx and Tx rings. */ +static void +pcnet32_init_ring(struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + int i; + + lp->lock = 0, lp->tx_full = 0; + lp->cur_rx = lp->cur_tx = 0; + lp->dirty_rx = lp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ)); + lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); + lp->rx_ring[i].status = le16_to_cpu(0x8000); + } + /* The Tx buffer address is filled in as needed, but we do need to clear + the upper ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_ring[i].base = 0; + lp->tx_ring[i].status = 0; + } + + lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS; + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = dev->dev_addr[i]; + lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring)); + lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring)); +} + +static void +pcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit) +{ + int i; + unsigned int ioaddr = dev->base_addr; + + pcnet32_purge_tx_ring(dev); + pcnet32_init_ring(dev); + + outw(0x0000, ioaddr + PCNET32_ADDR); + /* ReInit Ring */ + outw(0x0001, ioaddr + PCNET32_DATA); + i = 0; + while (i++ < 100) + if (inw(ioaddr+PCNET32_DATA) & 0x0100) + break; + + outw(csr0_bits, ioaddr + PCNET32_DATA); +} + +static int +pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + unsigned int ioaddr = dev->base_addr; + int entry; + unsigned long flags; + + /* Transmitter timeout, serious problems. */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 20) + return 1; + outw(0, ioaddr+PCNET32_ADDR); + printk("%s: transmit timed out, status %4.4x, resetting.\n", + dev->name, inw(ioaddr+PCNET32_DATA)); + outw(0x0004, ioaddr+PCNET32_DATA); + lp->stats.tx_errors++; +#ifndef final_version + { + int i; + printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", + lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", + lp->cur_rx); + for (i = 0 ; i < RX_RING_SIZE; i++) + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, + lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status); + for (i = 0 ; i < TX_RING_SIZE; i++) + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + lp->tx_ring[i].base, -lp->tx_ring[i].length, + lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status); + printk("\n"); + } +#endif + pcnet32_restart(dev, 0x0042, 1); + + dev->tbusy = 0; + dev->trans_start = jiffies; + + return 0; + } + + if (pcnet32_debug > 3) { + outw(0x0000, ioaddr+PCNET32_ADDR); + printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name, + inw(ioaddr+PCNET32_DATA)); + outw(0x0000, ioaddr+PCNET32_DATA); + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + printk("%s: Transmitter access conflict.\n", dev->name); + return 1; + } + + if (test_and_set_bit(0, (void*)&lp->lock) != 0) { + if (pcnet32_debug > 0) + printk("%s: tx queue lock!.\n", dev->name); + /* don't clear dev->tbusy flag. */ + return 1; + } + + /* Fill in a Tx ring entry */ + + /* Mask to ring buffer boundary. */ + entry = lp->cur_tx & TX_RING_MOD_MASK; + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + lp->tx_ring[entry].length = le16_to_cpu(-skb->len); + + lp->tx_ring[entry].misc = 0x00000000; + + lp->tx_skbuff[entry] = skb; + lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data)); + lp->tx_ring[entry].status = le16_to_cpu(0x8300); + + lp->cur_tx++; + + /* Trigger an immediate send poll. */ + outw(0x0000, ioaddr+PCNET32_ADDR); + outw(0x0048, ioaddr+PCNET32_DATA); + + dev->trans_start = jiffies; + + save_flags(flags); + cli(); + lp->lock = 0; + if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) + clear_bit(0, (void*)&dev->tbusy); + else + lp->tx_full = 1; + restore_flags(flags); + + return 0; +} + +/* The PCNET32 interrupt handler. */ +static void +pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct device *dev = (struct device *)dev_id; + struct pcnet32_private *lp; + unsigned int csr0, ioaddr; + int boguscnt = max_interrupt_work; + int must_restart; + + if (dev == NULL) { + printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + ioaddr = dev->base_addr; + lp = (struct pcnet32_private *)dev->priv; + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + + dev->interrupt = 1; + + outw(0x00, dev->base_addr + PCNET32_ADDR); + while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 + && --boguscnt >= 0) { + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA); + + must_restart = 0; + + if (pcnet32_debug > 5) + printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", + dev->name, csr0, inw(dev->base_addr + PCNET32_DATA)); + + if (csr0 & 0x0400) /* Rx interrupt */ + pcnet32_rx(dev); + + if (csr0 & 0x0200) { /* Tx-done interrupt */ + int dirty_tx = lp->dirty_tx; + + while (dirty_tx < lp->cur_tx) { + int entry = dirty_tx & TX_RING_MOD_MASK; + int status = (short)le16_to_cpu(lp->tx_ring[entry].status); + + if (status < 0) + break; /* It still hasn't been Txed */ + + lp->tx_ring[entry].base = 0; + + if (status & 0x4000) { + /* There was an major error, log it. */ + int err_status = le16_to_cpu(lp->tx_ring[entry].misc); + lp->stats.tx_errors++; + if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; + if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; + if (err_status & 0x10000000) lp->stats.tx_window_errors++; + if (err_status & 0x40000000) { + /* Ackk! On FIFO errors the Tx unit is turned off! */ + lp->stats.tx_fifo_errors++; + /* Remove this verbosity later! */ + printk("%s: Tx FIFO error! Status %4.4x.\n", + dev->name, csr0); + /* Restart the chip. */ + must_restart = 1; + } + } else { + if (status & 0x1800) + lp->stats.collisions++; + lp->stats.tx_packets++; + } + + /* We must free the original skb */ + if (lp->tx_skbuff[entry]) { + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + } + dirty_tx++; + } + +#ifndef final_version + if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { + printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dirty_tx, lp->cur_tx, lp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (lp->tx_full && dev->tbusy + && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + /* The ring is no longer full, clear tbusy. */ + lp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + + lp->dirty_tx = dirty_tx; + } + + /* Log misc errors. */ + if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */ + if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */ + if (csr0 & 0x0800) { + printk("%s: Bus master arbitration failure, status %4.4x.\n", + dev->name, csr0); + /* Restart the chip. */ + must_restart = 1; + } + + if (must_restart) { + /* stop the chip to clear the error condition, then restart */ + outw(0x0000, dev->base_addr + PCNET32_ADDR); + outw(0x0004, dev->base_addr + PCNET32_DATA); + pcnet32_restart(dev, 0x0002, 0); + } + } + + /* Clear any other interrupt, and set interrupt enable. */ + outw(0x0000, dev->base_addr + PCNET32_ADDR); + outw(0x7940, dev->base_addr + PCNET32_DATA); + + if (pcnet32_debug > 4) + printk("%s: exiting interrupt, csr%d=%#4.4x.\n", + dev->name, inw(ioaddr + PCNET32_ADDR), + inw(dev->base_addr + PCNET32_DATA)); + + dev->interrupt = 0; + return; +} + +static int +pcnet32_rx(struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + int entry = lp->cur_rx & RX_RING_MOD_MASK; + int i; + + /* If we own the next entry, it's a new packet. Send it up. */ + while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) { + int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8; + + if (status != 0x03) { /* There was an error. */ + /* There is a tricky error noted by John Murphy, + to Russ Nelson: Even with full-sized + buffers it's possible for a jabber packet to use two + buffers, with only the last correctly noting the error. */ + if (status & 0x01) /* Only count a general error at the */ + lp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x20) lp->stats.rx_frame_errors++; + if (status & 0x10) lp->stats.rx_over_errors++; + if (status & 0x08) lp->stats.rx_crc_errors++; + if (status & 0x04) lp->stats.rx_fifo_errors++; + lp->rx_ring[entry].status &= le16_to_cpu(0x03ff); + } + else + { + /* Malloc up new buffer, compatible with net-2e. */ + short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4; + struct sk_buff *skb; + + if(pkt_len < 60) { + printk("%s: Runt packet!\n",dev->name); + lp->stats.rx_errors++; + } else { + skb = dev_alloc_skb(pkt_len+2); + if (skb == NULL) { + printk("%s: Memory squeeze, deferring packet.\n", + dev->name); + for (i=0; i < RX_RING_SIZE; i++) + if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0) + break; + + if (i > RX_RING_SIZE -2) + { + lp->stats.rx_dropped++; + lp->rx_ring[entry].status |= le16_to_cpu(0x8000); + lp->cur_rx++; + } + break; + } + skb->dev = dev; + skb_reserve(skb,2); /* 16 byte align */ + skb_put(skb,pkt_len); /* Make room */ + eth_copy_and_sum(skb, + (unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)), + pkt_len,0); + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + } + } + /* The docs say that the buffer length isn't touched, but Andrew Boyd + of QNX reports that some revs of the 79C965 clear it. */ + lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ); + lp->rx_ring[entry].status |= le16_to_cpu(0x8000); + entry = (++lp->cur_rx) & RX_RING_MOD_MASK; + } + + /* We should check that at least two ring entries are free. If not, + we should free one and mark stats->rx_dropped++. */ + + return 0; +} + +static int +pcnet32_close(struct device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + + dev->start = 0; + set_bit(0, (void*)&dev->tbusy); + + outw(112, ioaddr+PCNET32_ADDR); + lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA); + + outw(0, ioaddr+PCNET32_ADDR); + + if (pcnet32_debug > 1) + printk("%s: Shutting down ethercard, status was %2.2x.\n", + dev->name, inw(ioaddr+PCNET32_DATA)); + + /* We stop the PCNET32 here -- it occasionally polls + memory if we don't. */ + outw(0x0004, ioaddr+PCNET32_DATA); + + free_irq(dev->irq, dev); + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics *pcnet32_get_stats(struct device *dev) +{ + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + unsigned int ioaddr = dev->base_addr; + unsigned short saved_addr; + unsigned long flags; + + save_flags(flags); + cli(); + saved_addr = inw(ioaddr+PCNET32_ADDR); + outw(112, ioaddr+PCNET32_ADDR); + lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA); + outw(saved_addr, ioaddr+PCNET32_ADDR); + restore_flags(flags); + + return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + */ + +static void pcnet32_set_multicast_list(struct device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + + if (dev->flags&IFF_PROMISC) { + /* Log any net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + lp->init_block.mode = 0x8000; + } else { + int num_addrs=dev->mc_count; + if(dev->flags&IFF_ALLMULTI) + num_addrs=1; + /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ + memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter)); + lp->init_block.mode = 0x0000; + } + + outw(0, ioaddr+PCNET32_ADDR); + outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance. */ + + pcnet32_restart(dev, 0x0042, 0); /* Resume normal operation */ + +} + + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("AMD PCnet/PCI ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "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) +{ + if (debug >= 0) + pcnet32_debug = debug; + +#ifdef CARDBUS + register_driver(&pcnet32_ops); + return 0; +#else + return pcnet32_probe(NULL); +#endif +} + +void +cleanup_module(void) +{ + struct device *next_dev; + +#ifdef CARDBUS + unregister_driver(&pcnet32_ops); +#endif + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_pcnet32_dev) { + next_dev = ((struct pcnet32_private *)root_pcnet32_dev->priv)->next_module; + unregister_netdev(root_pcnet32_dev); + release_region(root_pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); + kfree(root_pcnet32_dev); + root_pcnet32_dev = next_dev; + } +} + +#endif /* MODULE */ + + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c pcnet32.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/ppp.c linux/drivers/net/ppp.c --- v2.0.33/linux/drivers/net/ppp.c Tue Aug 12 14:15:56 1997 +++ linux/drivers/net/ppp.c Wed Jun 3 15:17:47 1998 @@ -5,8 +5,9 @@ * * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid + * Fixed (I hope) the wait_queue trashing bug. Alan Cox * - * ==FILEVERSION 970703== + * ==FILEVERSION 980512== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -448,8 +449,13 @@ ppp->ubuf = NULL; ppp->cbuf = NULL; ppp->slcomp = NULL; +#if 0 + /* AC - We don't want to initialise this as the wait queue may still + be live. Having someone waiting on the old and the new queue is fine + the old people will unhook themselves so just set this up in ppp_alloc */ ppp->read_wait = NULL; ppp->write_wait = NULL; +#endif ppp->last_xmit = jiffies - flag_time; /* clear statistics */ @@ -798,9 +804,7 @@ } if (ppp == NULL) { - if (ppp->flags & SC_DEBUG) - printk (KERN_ERR - "ppp_tty_open: couldn't allocate ppp channel\n"); + printk (KERN_WARNING "ppp_tty_open: couldn't allocate ppp channel\n"); return -ENFILE; } /* diff -u --recursive --new-file v2.0.33/linux/drivers/net/rtl8139.c linux/drivers/net/rtl8139.c --- v2.0.33/linux/drivers/net/rtl8139.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/rtl8139.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,1301 @@ +/* rtl8139.c: A RealTek RTL8129/8139 Fast Ethernet driver for Linux. */ +/* + Written 1997-1998 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + All other rights reserved. + + This driver is for boards based on the RTL8129 and RTL8139 PCI ethernet + chips. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/rtl8139.html + + Twister-tuning code contributed by Kinston . +*/ + +static const char *version = +"rtl8139.c:v0.99B 4/7/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; + +/* Size of the in-memory receive ring. */ +#define RX_BUF_LEN_IDX 3 /* 0==8K, 1==16K, 2==32K, 3==64K */ +#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE 1536 + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* 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 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((4000*HZ)/1000) + +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include +#include + +#include +#include +#include + +#define RUN_AT(x) (jiffies + (x)) + +#include + +#if (LINUX_VERSION_CODE < 0x20123) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#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 + +I. Board Compatibility + +This device driver is designed for the RealTek RTL8129, the RealTek Fast +Ethernet controllers for PCI. This chip is used on a few clone boards. + + +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 will assign the +PCI INTA signal to a (preferably otherwise unused) system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Rx Ring buffers + +The receive unit uses a single linear ring buffer rather than the more +common (and more efficient) descriptor-based architecture. Incoming frames +are sequentially stored into the Rx region, and the host copies them into +skbuffs. + +Comment: While it is theoretically possible to process many frames in place, +any delay in Rx processing would cause us to drop frames. More importantly, +the Linux protocol stack is not designed to operate in this manner. + +IIIb. Tx operation + +The RTL8129 uses a fixed set of four Tx descriptors in register space. +In a stunningly bad design choice, Tx frames must be 32 bit aligned. Linux +aligns the IP header on word boundaries, and 14 byte ethernet header means +that almost all frames will need to be copied to an alignment buffer. + +IVb. References + +http://www.realtek.com.tw/cn/cn.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +*/ + +#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. */ + +/* Symbolic offsets to registers. */ +enum RTL8129_registers { + MAC0=0, /* Ethernet hardware address. */ + MAR0=8, /* Multicast filter. */ + TxStat0=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, + IntrMask=0x3C, IntrStatus=0x3E, + TxConfig=0x40, RxConfig=0x44, + Timer=0x48, /* A general-purpose counter. */ + RxMissed=0x4C, /* 24 bits valid, write clears. */ + 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, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS=0x70, /* FIFO Test Mode Select */ + CSCR=0x74, /* Chip Status and Configuration Register. */ + PARA78=0x78, PARA7c=0x7c, /* Magic transceiver parameter register. */ +}; + +enum ChipCmdBits { + CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, }; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr=0x8000, PCSTimeout=0x4000, + RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10, + TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01, +}; +enum TxStatusBits { + TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000, + TxOutOfWindow=0x20000000, TxAborted=0x40000000, TxCarrierLost=0x80000000, +}; +enum RxStatusBits { + RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000, + RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004, + RxBadAlign=0x0002, RxStatusOK=0x0001, +}; + +enum CSCRBits { + CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800, + CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0, + CSCR_LinkDownCmd=0x0f3c0, +}; + +/* Twister tuning parameters from RealTek. Completely undocumented. */ +unsigned long param[4][4]={ + {0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43}, + {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83}, + {0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83}, + {0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83} +}; + +struct rtl8129_private { + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct device *next_module; + int chip_id; + int chip_revision; +#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; + /* 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 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. */ + unsigned int mediasense:1; /* Media sensing in progress. */ +}; + +#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 +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(max_interrupt_work, "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 void rtl8129_timer(unsigned long data); +static void rtl8129_tx_timeout(struct device *dev); +static void rtl8129_init_ring(struct device *dev); +static int rtl8129_start_xmit(struct sk_buff *skb, struct device *dev); +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 struct enet_statistics *rtl8129_get_stats(struct device *dev); +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 + +int rtl8139_probe(struct device *dev) +{ + int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ + + /* 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_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) + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + if (vendor != PCI_VENDOR_ID_REALTEK) + continue; + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + 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; + + /* 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); + } + +#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 + + 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 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 int did_version = 0; /* Already printed version info. */ + struct rtl8129_private *tp; + int i; + + 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); + + /* 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); + + 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"); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Some data structures must be quadword aligned. */ + tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); + 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; + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ + if (chip_id == 0x8129) { + 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); + + if (mii_status != 0xffff && mii_status != 0x0000) { + tp->phys[phy_idx++] = phy; + printk(KERN_INFO "%s: MII transceiver found at address %d.\n", + dev->name, phy); + } + } + if (phy_idx == 0) { + printk(KERN_INFO "%s: No MII transceivers found! Assuming SYM " + "transceiver.\n", + dev->name); + tp->phys[0] = -1; + } + } else { + tp->phys[0] = -1; + } + + /* Put the chip into low-power mode. */ + outb(0xC0, ioaddr + Cfg9346); + outb(0x03, ioaddr + Config1); + 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 (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]; + } +#endif + + /* The Rtl8129-specific entries in the device structure. */ + dev->open = &rtl8129_open; + dev->hard_start_xmit = &rtl8129_start_xmit; + dev->stop = &rtl8129_close; + dev->get_stats = &rtl8129_get_stats; + dev->set_multicast_list = &set_rx_mode; + + return dev; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz is untested. + */ + +#ifdef _LINUX_DELAY_H +#define eeprom_delay(nanosec) udelay(1) +#else +#define eeprom_delay(nanosec) do { ; } while (0) +#endif + +/* 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) +{ + int i; + unsigned retval = 0; + int ee_addr = ioaddr + Cfg9346; + int read_cmd = location | EE_READ_CMD; + + outb(EE_ENB & ~EE_CS, ee_addr); + outb(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + 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); + 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); + } + outb(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outb(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(100); + retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); + outb(EE_ENB, ee_addr); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outb(~EE_CS, ee_addr); + return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + 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_DIR 0x80 +#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 + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync(int ioaddr) +{ + int i; + int mdio_addr = ioaddr + MII_SMI; + + for (i = 32; i >= 0; i--) { + outb(MDIO_DIR | MDIO_DATA_OUT, mdio_addr); + mdio_delay(); + outb(MDIO_DIR | MDIO_DATA_OUT | MDIO_CLK, mdio_addr); + mdio_delay(); + } + return; +} +static int mdio_read(int ioaddr, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + int mdio_addr = ioaddr + MII_SMI; + + mdio_sync(ioaddr); + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = + (read_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + + outb(MDIO_DIR | dataval, mdio_addr); + mdio_delay(); + outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr); + mdio_delay(); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outb(0, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 0); + outb(MDIO_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + +static int +rtl8129_open(struct device *dev) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + int full_duplex = 0; + + /* Soft reset the chip. */ + outb(CmdReset, ioaddr + ChipCmd); + + if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) { + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + tp->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL); + tp->rx_ring = kmalloc(RX_BUF_LEN + 16, GFP_KERNEL); + if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { + if (tp->tx_bufs) + kfree(tp->tx_bufs); + if (rtl8129_debug > 0) + printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", + dev->name, RX_BUF_LEN); + return -ENOMEM; + } + rtl8129_init_ring(dev); + + /* 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); + + /* 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)|0x03000000, ioaddr + TxConfig); + + 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); + if (mii_reg5 == 0xffff) + ; /* Not there */ + else if ((mii_reg5 & 0x0100) == 0x0100 + || (mii_reg5 & 0x00C0) == 0x0040) + 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); + } + + outb(0xC0, ioaddr + Cfg9346); + outb(full_duplex ? 0x60 : 0x20, ioaddr + Config1); + outb(0x00, ioaddr + Cfg9346); + + outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf); + + /* Start the chip's Tx and Rx process. */ + outl(0, ioaddr + RxMissed); + set_rx_mode(dev); + + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Enable all known interrupts by setting the interrupt mask. */ + outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver + | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); + + if (rtl8129_debug > 1) + printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %4.4x IRQ %d" + " GP Pins %2.2x %s-duplex.\n", + dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), + full_duplex ? "full" : "half"); + + /* Set the timer to switch to check for link beat and perhaps switch + to an alternate media type. */ + init_timer(&tp->timer); + tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.data = (unsigned long)dev; + tp->timer.function = &rtl8129_timer; /* timer handler */ + add_timer(&tp->timer); + + return 0; +} + +static void rtl8129_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + int next_tick = 0; + + 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 (rtl8129_debug > 2) { + if (tp->chip_id == 0x8129) + printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", + dev->name, inb(ioaddr + GPPinData)); + else + printk(KERN_DEBUG"%s: Media selection tick, Link partner %4.4x.\n", + dev->name, inw(ioaddr + NWayLPAR)); + printk(KERN_DEBUG"%s: Other registers are IntMask %4.4x IntStatus %4.4x" + " RxStatus %4.4x.\n", + dev->name, inw(ioaddr + IntrMask), inw(ioaddr + IntrStatus), + inl(ioaddr + RxEarlyStatus)); + printk(KERN_DEBUG"%s: Chip config %2.2x %2.2x.\n", + dev->name, inb(ioaddr + Config0), inb(ioaddr + Config1)); + } + + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void rtl8129_tx_timeout(struct device *dev) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + int i; + + /* 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. */ + 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), + 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)); + } + + /* Soft reset the chip. */ + outb(CmdReset, ioaddr + ChipCmd); + for (i = 0; i < 6; i++) + outb(dev->dev_addr[i], ioaddr + MAC0 + i); + + { /* 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]; + tp->dirty_tx = tp->cur_tx = 0; + + for (i = 0; i < j; i++) { + skb = tp->tx_skbuff[i] = saved_skb[i]; + if ((long)skb->data & 3) { /* Must use alignment buffer. */ + memcpy(tp->tx_buf[i], skb->data, skb->len); + outl(virt_to_bus(tp->tx_buf[i]), ioaddr + TxAddr0 + i*4); + } 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); + } + 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; + } 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. */ + outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver + | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +rtl8129_init_ring(struct device *dev) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + tp->tx_skbuff[i] = 0; + tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; + } +} + +static int +rtl8129_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + int entry; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + rtl8129_tx_timeout(dev); + return 1; + } + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % NUM_TX_DESC; + + tp->tx_skbuff[entry] = skb; + if ((long)skb->data & 3) { /* Must use alignment buffer. */ + memcpy(tp->tx_buf[entry], skb->data, skb->len); + outl(virt_to_bus(tp->tx_buf[entry]), ioaddr + TxAddr0 + entry*4); + } 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); + + if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ + dev->tbusy = 0; + } 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); + + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +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; + int status; + + if (dev == NULL) { + printk (KERN_ERR"rtl8139_interrupt(): IRQ %d for unknown device.\n", + irq); + return; + } + + ioaddr = dev->base_addr; + tp = (struct rtl8129_private *)dev->priv; + if (test_and_set_bit(0, (void*)&tp->in_interrupt)) { + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + dev->interrupt = 1; + + do { + status = inw(ioaddr + IntrStatus); + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(status, ioaddr + IntrStatus); + + if (rtl8129_debug > 4) + printk(KERN_DEBUG"%s: interrupt status=%#4.4x new intstat=%#4.4x.\n", + dev->name, status, inw(ioaddr + IntrStatus)); + + if ((status & (PCIErr|PCSTimeout|RxUnderrun|RxOverflow|RxFIFOOver + |TxErr|TxOK|RxErr|RxOK)) == 0) + break; + + if (status & (RxOK|RxUnderrun|RxOverflow|RxFIFOOver))/* Rx interrupt */ + rtl8129_rx(dev); + + if (status & (TxOK | TxErr)) { + unsigned int dirty_tx; + + 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); + + if ( ! (txstatus & TxHostOwns)) + break; /* It still hasn't been Txed */ + + /* Note: TxCarrierLost is always asserted at 100mbps. */ + if (txstatus & (TxOutOfWindow | TxAborted)) { + /* There was an major error, log it. */ +#ifndef final_version + if (rtl8129_debug > 1) + printk(KERN_NOTICE"%s: Transmit error, Tx status %8.8x.\n", + dev->name, txstatus); +#endif + tp->stats.tx_errors++; + if (txstatus&TxAborted) { + tp->stats.tx_aborted_errors++; + outl((TX_DMA_BURST<<8)|0x03000001, ioaddr + TxConfig); + } + if (txstatus&TxCarrierLost) tp->stats.tx_carrier_errors++; + if (txstatus&TxOutOfWindow) tp->stats.tx_window_errors++; +#ifdef ETHER_STATS + if ((txstatus & 0x0f000000) == 0x0f000000) + tp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + /* No count for tp->stats.tx_deferred */ +#endif + if (txstatus & TxUnderrun) { + /* Todo: increase the Tx FIFO threshold. */ + tp->stats.tx_fifo_errors++; + } + tp->stats.collisions += (txstatus >> 24) & 15; +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.tx_bytes += txstatus & 0x7ff; +#endif + tp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); + tp->tx_skbuff[entry] = 0; + } + +#ifndef final_version + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { + printk(KERN_ERR"%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); + dirty_tx += NUM_TX_DESC; + } +#endif + + if (tp->tx_full && dev->tbusy + && dirty_tx > tp->cur_tx - NUM_TX_DESC) { + /* The ring is no longer full, clear tbusy. */ + tp->tx_full = 0; + dev->tbusy = 0; + mark_bh(NET_BH); + } + + tp->dirty_tx = dirty_tx; + } + + /* Check uncommon events with one test. */ + if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver + |TxErr|RxErr)) { + /* Update the error count. */ + tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); + outl(0, ioaddr + RxMissed); + + if (status & (RxUnderrun | RxOverflow | RxErr | RxFIFOOver)) + tp->stats.rx_errors++; + + if (status & (PCSTimeout)) tp->stats.rx_length_errors++; + if (status & (RxUnderrun|RxFIFOOver)) tp->stats.rx_fifo_errors++; + if (status & RxOverflow) { + tp->stats.rx_over_errors++; + tp->cur_rx = inw(ioaddr + RxBufAddr) % RX_BUF_LEN; + outw(tp->cur_rx - 16, ioaddr + RxBufPtr); + } + /* Error sources cleared above. */ + } + if (--boguscnt < 0) { + printk(KERN_WARNING"%s: Too much work at interrupt, " + "IntrStatus=0x%4.4x.\n", + dev->name, status); + /* Clear all interrupt sources. */ + outw(0xffff, ioaddr + IntrStatus); + break; + } + } while (1); + + if (rtl8129_debug > 3) + printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", + dev->name, inl(ioaddr + IntrStatus)); + + dev->interrupt = 0; + clear_bit(0, (void*)&tp->in_interrupt); + 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) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + unsigned char *rx_ring = tp->rx_ring; + u16 cur_rx = tp->cur_rx; + + if (rtl8129_debug > 4) + printk(KERN_DEBUG"%s: In rtl8129_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", + dev->name, cur_rx, inw(ioaddr + RxBufAddr), + inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd)); + + while ((inb(ioaddr + ChipCmd) & 1) == 0) { + int ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status = *(u32*)(rx_ring + ring_offset); + int rx_size = rx_status >> 16; + + if (rtl8129_debug > 4) { + int i; + printk(KERN_DEBUG"%s: rtl8129_rx() status %4.4x, size %4.4x, cur %4.4x.\n", + dev->name, rx_status, rx_size, cur_rx); + printk(KERN_DEBUG"%s: Frame contents ", dev->name); + for (i = 0; i < 70; i++) + printk(" %2.2x", rx_ring[ring_offset + i]); + printk(".\n"); + } + if (rx_status & RxTooLong) { + if (rtl8129_debug > 0) + printk(KERN_NOTICE"%s: Oversized Ethernet frame, status %4.4x!\n", + dev->name, rx_status); + tp->stats.rx_length_errors++; + } else if (rx_status & + (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) { + if (rtl8129_debug > 1) + printk(KERN_DEBUG"%s: Ethernet frame had errors," + " status %4.4x.\n", dev->name, rx_status); + tp->stats.rx_errors++; + if (rx_status & (RxBadSymbol|RxBadAlign)) + tp->stats.rx_frame_errors++; + if (rx_status & (RxRunt|RxTooLong)) tp->stats.rx_length_errors++; + if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + tp->cur_rx = 0; + outb(CmdTxEnb, ioaddr + ChipCmd); + outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | + (RX_DMA_BURST<<8), ioaddr + RxConfig); + } else { + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + struct sk_buff *skb; + + skb = dev_alloc_skb(rx_size + 2); + if (skb == NULL) { + printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", + dev->name); + /* We should check that some rx space is free. + If not, free one and mark stats->rx_dropped++. */ + tp->stats.rx_dropped++; + break; + } + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP fields. */ + if (ring_offset+rx_size+4 > RX_BUF_LEN) { + int semi_count = RX_BUF_LEN - ring_offset - 4; + memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4], + semi_count); + memcpy(skb_put(skb, rx_size-semi_count), rx_ring, + rx_size-semi_count); + if (rtl8129_debug > 4) { + int i; + printk(KERN_DEBUG"%s: Frame wrap @%d", + dev->name, semi_count); + for (i = 0; i < 16; i++) + printk(" %2.2x", rx_ring[i]); + printk(".\n"); + memset(rx_ring, 0xcc, 16); + } + } else + memcpy(skb_put(skb, rx_size), &rx_ring[ring_offset + 4], + rx_size); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); +#if LINUX_VERSION_CODE > 0x20119 + tp->stats.rx_bytes += rx_size; +#endif + tp->stats.rx_packets++; + } + + cur_rx += rx_size + 4; + cur_rx = (cur_rx + 3) & ~3; + outw(cur_rx - 16, ioaddr + RxBufPtr); + } + if (rtl8129_debug > 4) + printk(KERN_DEBUG"%s: Done rtl8129_rx(), current %4.4x BufAddr %4.4x," + " free to %4.4x, Cmd %2.2x.\n", + dev->name, cur_rx, inw(ioaddr + RxBufAddr), + inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd)); + tp->cur_rx = cur_rx; + return 0; +} + +static int +rtl8129_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (rtl8129_debug > 1) + printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", + dev->name, inw(ioaddr + IntrStatus)); + + /* Disable interrupts by clearing the interrupt mask. */ + outw(0x0000, ioaddr + IntrMask); + + /* Stop the chip's Tx and Rx DMA processes. */ + outb(0x00, ioaddr + ChipCmd); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); + outl(0, ioaddr + RxMissed); + + del_timer(&tp->timer); + + free_irq(dev->irq, dev); + + for (i = 0; i < NUM_TX_DESC; i++) { + if (tp->tx_skbuff[i]) + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); + tp->tx_skbuff[i] = 0; + } + kfree(tp->rx_ring); + kfree(tp->tx_bufs); + + /* Green! Put the chip in low-power mode. */ + outb(0xC0, ioaddr + Cfg9346); + outb(0x03, ioaddr + Config1); + outb('H', ioaddr + HltClk); /* 'R' would leave the clock running. */ + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +rtl8129_get_stats(struct device *dev) +{ + struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; + int ioaddr = dev->base_addr; + + if (dev->start) { + tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); + outl(0, ioaddr + RxMissed); + } + + return &tp->stats; +} + +/* 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) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + 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; +} + +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; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* 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)) { + /* 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; + } else { + struct dev_mc_list *mclist; + + memset(mc_filter, 0, sizeof(mc_filter)); + 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); + } + /* 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)); + return; +} + +#ifdef MODULE + +/* An additional parameter that may be passed in... */ +static int debug = -1; + +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; +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* 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; + unregister_netdev(root_rtl8129_dev); + release_region(root_rtl8129_dev->base_addr, RTL8129_TOTAL_SIZE); + kfree(root_rtl8129_dev); + root_rtl8129_dev = next_dev; + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c rtl8139.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/sdla.c linux/drivers/net/sdla.c --- v2.0.33/linux/drivers/net/sdla.c Tue Apr 8 08:47:46 1997 +++ linux/drivers/net/sdla.c Wed Jun 3 15:17:47 1998 @@ -32,6 +32,7 @@ * 2 of the License, or (at your option) any later version. */ +#include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c --- v2.0.33/linux/drivers/net/sk_g16.c Mon May 6 02:26:09 1996 +++ linux/drivers/net/sk_g16.c Wed Jun 3 15:17:47 1998 @@ -778,16 +778,16 @@ dev->dev_addr[4], dev->dev_addr[5]); - /* Grab the I/O Port region */ - request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); - - /* Initialize device structure */ - /* Allocate memory for private structure */ p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL); - if (p == NULL) + if (p == NULL) { + printk("%s: ERROR - no memory for driver data!\n", dev->name); return -ENOMEM; + } memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */ + + /* Grab the I/O Port region */ + request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); /* Assign our Device Driver functions */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c --- v2.0.33/linux/drivers/net/smc-ultra.c Wed Jun 5 23:54:05 1996 +++ linux/drivers/net/smc-ultra.c Wed Jun 3 15:17:47 1998 @@ -2,7 +2,7 @@ /* This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. - Written 1993-1996 by Donald Becker. + Written 1993-1998 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. @@ -14,7 +14,7 @@ Center of Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - This driver uses the cards in the 8390-compatible, shared memory mode. + This driver uses the cards in the 8390-compatible mode. Most of the run-time complexity is handled by the generic code in 8390.c. The code in this file is responsible for @@ -27,6 +27,8 @@ ultra_block_input() Routines for reading and writing blocks of ultra_block_output() packet buffer memory. + ultra_pio_input() + ultra_pio_output() This driver enables the shared memory only when doing the actual data transfers to avoid a bug in early version of the card that corrupted @@ -34,7 +36,7 @@ This driver now supports the programmed-I/O (PIO) data transfer mode of the EtherEZ. It does not use the non-8390-compatible "Altego" mode. - That support (if available) is smc-ez.c. + That support (if available) is in smc-ez.c. Changelog: @@ -44,8 +46,7 @@ */ static const char *version = - "smc-ultra.c:v2.00 6/6/96 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - + "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; #include @@ -69,18 +70,18 @@ static int ultra_open(struct device *dev); static void ultra_reset_8390(struct device *dev); -static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void ultra_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void ultra_block_output(struct device *dev, int count, const unsigned char *buf, const start_page); -static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void ultra_pio_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset); static void ultra_pio_output(struct device *dev, int count, - const unsigned char *buf, const start_page); + const unsigned char *buf, const start_page); static int ultra_close_card(struct device *dev); @@ -151,11 +152,8 @@ if ((checksum & 0xff) != 0xFF) return ENODEV; - /* We should have a "dev" from Space.c or the static module table. */ - if (dev == NULL) { - printk("smc-ultra.c: Passed a NULL device.\n"); + if (dev == NULL) dev = init_etherdev(0, 0); - } if (ei_debug && version_printed++ == 0) printk(version); @@ -201,7 +199,7 @@ printk (", no memory for dev->priv.\n"); return -ENOMEM; } - + /* OK, we are certain this is going to work. Setup the device. */ request_region(ioaddr, ULTRA_IO_EXTENT, model_name); @@ -251,12 +249,19 @@ ultra_open(struct device *dev) { int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ + unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, + 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; - if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL)) + if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) return -EAGAIN; outb(0x00, ioaddr); /* Disable shared memory for safety. */ outb(0x80, ioaddr + 5); + /* Set the IRQ line. */ + outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); + outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); + outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); + if (ei_status.block_input == &ultra_pio_input) { outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */ outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */ @@ -354,10 +359,10 @@ byte-sequentially to IOPA, with no intervening I/O operations, and the data is read or written to the IOPD data port. The only potential complication is that the address register is shared - must be always be rewritten between each read/write direction change. + and must be always be rewritten between each read/write direction change. This is no problem for us, as the 8390 code ensures that we are single threaded. */ -static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ @@ -375,12 +380,8 @@ /* For now set the address again, although it should already be correct. */ outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */ outb(ring_offset >> 8, ioaddr + IOPA); + /* We know skbuffs are padded to at least word alignment. */ insw(ioaddr + IOPD, buf, (count+1)>>1); -#ifdef notdef - /* We don't need this -- skbuffs are padded to at least word alignment. */ - if (count & 0x01) { - buf[count-1] = inb(ioaddr + IOPD); -#endif } static void ultra_pio_output(struct device *dev, int count, @@ -389,6 +390,7 @@ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ outb(start_page, ioaddr + IOPA); + /* An extra odd byte is OK here as well. */ outsw(ioaddr + IOPD, buf, (count+1)>>1); } @@ -404,7 +406,7 @@ printk("%s: Shutting down ethercard.\n", dev->name); outb(0x00, ioaddr + 6); /* Disable interrupts. */ - free_irq(dev->irq, NULL); + free_irq(dev->irq, dev); irq2dev_map[dev->irq] = 0; NS8390_init(dev, 0); @@ -488,6 +490,7 @@ * version-control: t * kept-new-versions: 5 * c-indent-level: 4 + * c-basic-offset: 4 * tab-width: 4 * End: */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/smc-ultra32.c linux/drivers/net/smc-ultra32.c --- v2.0.33/linux/drivers/net/smc-ultra32.c Tue Aug 12 16:04:57 1997 +++ linux/drivers/net/smc-ultra32.c Wed Jun 3 15:17:47 1998 @@ -238,8 +238,9 @@ static int ultra32_open(struct device *dev) { int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ + int irq_flags = (inb(ioaddr + ULTRA32_CFG5) & 0x08) ? 0 : SA_SHIRQ; - if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL)) + if (request_irq(dev->irq, ei_interrupt, irq_flags, ei_status.name, dev)) return -EAGAIN; outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ diff -u --recursive --new-file v2.0.33/linux/drivers/net/strip.c linux/drivers/net/strip.c --- v2.0.33/linux/drivers/net/strip.c Sat Aug 17 11:19:26 1996 +++ linux/drivers/net/strip.c Wed Jun 3 15:17:47 1998 @@ -73,7 +73,6 @@ #include #endif -#include #include #include #include diff -u --recursive --new-file v2.0.33/linux/drivers/net/tlan.c linux/drivers/net/tlan.c --- v2.0.33/linux/drivers/net/tlan.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/tlan.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,2703 @@ +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.c + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4 and colums>=132. + * + ** Useful (if not required) reading: + * + * Texas Instruments, ThunderLAN Programmer's Guide, + * TI Literature Number SPWU013A + * available in PDF format from www.ti.com + * National Semiconductor, DP83840A Data Sheet + * available in PDF format from www.national.com + * Microchip Technology, 24C01A/02A/04A Data Sheet + * available in PDF format from www.microchip.com + * + ********************************************************************/ + + +#include + +#include "tlan.h" + +#include +#include +#include +#include +#include + + + +typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); + + +#ifdef MODULE + +static struct device *TLanDevices = NULL; +static int TLanDevicesInstalled = 0; + +#endif + + +static int debug = 0; +static int aui = 0; +static u8 *TLanPadBuffer; +static char TLanSignature[] = "TLAN"; +static int TLanVersionMajor = 0; +static int TLanVersionMinor = 38; + + +static TLanPciId TLanDeviceList[] = { + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10, + "Compaq Netelligent 10" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100, + "Compaq Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, + "Compaq Integrated NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P, + "Compaq NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETFLEX_3P_BNC, + "Compaq NetFlex-3/P" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, + "Compaq ProLiant Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, + "Compaq Dual Port Netelligent 10/100" + }, + { PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_DESKPRO_4000_5233MMX, + "Compaq Deskpro 4000 5233MMX" + }, + { 0, + 0, + NULL + } /* End of List */ +}; + + +static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); +static int TLan_Init( struct device * ); +static int TLan_Open(struct device *dev); +static int TLan_StartTx(struct sk_buff *, struct device *); +static void TLan_HandleInterrupt(int, void *, struct pt_regs *); +static int TLan_Close(struct device *); +static struct net_device_stats *TLan_GetStats( struct device * ); +static void TLan_SetMulticastList( struct device * ); + +static u32 TLan_HandleInvalid( struct device *, u16 ); +static u32 TLan_HandleTxEOF( struct device *, u16 ); +static u32 TLan_HandleStatOverflow( struct device *, u16 ); +static u32 TLan_HandleRxEOF( struct device *, u16 ); +static u32 TLan_HandleDummy( struct device *, u16 ); +static u32 TLan_HandleTxEOC( struct device *, u16 ); +static u32 TLan_HandleStatusCheck( struct device *, u16 ); +static u32 TLan_HandleRxEOC( struct device *, u16 ); + +static void TLan_Timer( unsigned long ); + +static void TLan_ResetLists( struct device * ); +static void TLan_PrintDio( u16 ); +static void TLan_PrintList( TLanList *, char *, int ); +static void TLan_ReadAndClearStats( struct device *, int ); +static int TLan_Reset( struct device * ); +static void TLan_SetMac( struct device *, int areg, char *mac ); + +static int TLan_PhyNop( struct device * ); +static void TLan_PhyPrint( struct device * ); +static void TLan_PhySelect( struct device * ); +static int TLan_PhyInternalCheck( struct device * ); +static int TLan_PhyInternalService( struct device * ); +static int TLan_PhyDp83840aCheck( struct device * ); + +static int TLan_MiiReadReg(u16, u16, u16, u16 *); +static void TLan_MiiSendData( u16, u32, unsigned ); +static void TLan_MiiSync(u16); +static void TLan_MiiWriteReg(u16, u16, u16, u16); + +static void TLan_EeSendStart( u16 ); +static int TLan_EeSendByte( u16, u8, int ); +static void TLan_EeReceiveByte( u16, u8 *, int ); +static int TLan_EeReadByte( u16, u8, u8 * ); + + +static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { + TLan_HandleInvalid, + TLan_HandleTxEOF, + TLan_HandleStatOverflow, + TLan_HandleRxEOF, + TLan_HandleDummy, + TLan_HandleTxEOC, + TLan_HandleStatusCheck, + TLan_HandleRxEOC +}; + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Primary Functions + + These functions are more or less common to all Linux network drivers. + +****************************************************************************** +*****************************************************************************/ + + +#ifdef MODULE + + /*************************************************************** + * init_module + * + * Returns: + * 0 if module installed ok, non-zero if not. + * Parms: + * None + * + * This function begins the setup of the driver creating a + * pad buffer, finding all TLAN devices (matching + * TLanDeviceList entries), and creating and initializing a + * device structure for each adapter. + * + **************************************************************/ + +extern int init_module(void) +{ + TLanPrivateInfo *priv; + u8 bus; + struct device *dev; + size_t dev_size; + u8 dfn; + u32 dl_ix; + int failed; + int found; + u32 io_base; + u8 irq; + u8 rev; + + printk( "TLAN driver, v%d.%d, (C) 1997 Caldera, Inc.\n", + TLanVersionMajor, + TLanVersionMinor + ); + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, + ( GFP_KERNEL | GFP_DMA ) + ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + return -ENOMEM; + } + + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + + dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); + + while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ) ) ) { + dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); + if ( dev == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + continue; + } + memset( dev, 0, dev_size ); + + dev->priv = priv = ( (void *) dev ) + sizeof(struct device); + dev->name = priv->devName; + strcpy( priv->devName, " " ); + dev->base_addr = io_base; + dev->irq = irq; + dev->init = TLan_Init; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + ether_setup( dev ); + + failed = register_netdev( dev ); + + if ( failed ) { + printk( "TLAN: Could not register network device. Freeing struct.\n" ); + kfree( dev ); + } else { + priv->nextDevice = TLanDevices; + TLanDevices = dev; + TLanDevicesInstalled++; + printk("TLAN: %s irq=%2d io=%04x, %s\n", dev->name, (int) irq, io_base, TLanDeviceList[dl_ix].deviceName ); + } + } + + // printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); + + return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); + +} /* init_module */ + + + + + /*************************************************************** + * cleanup_module + * + * Returns: + * Nothing + * Parms: + * None + * + * Goes through the TLanDevices list and frees the device + * structs and memory associated with each device (lists + * and buffers). It also ureserves the IO port regions + * associated with this device. + * + **************************************************************/ + +extern void cleanup_module(void) +{ + struct device *dev; + TLanPrivateInfo *priv; + + while (TLanDevicesInstalled) { + dev = TLanDevices; + priv = (TLanPrivateInfo *) dev->priv; + if ( priv->dmaStorage ) + kfree( priv->dmaStorage ); + release_region( dev->base_addr, 0x10 ); + unregister_netdev( dev ); + TLanDevices = priv->nextDevice; + kfree( dev ); + TLanDevicesInstalled--; + } + kfree( TLanPadBuffer ); + +} /* cleanup_module */ + + +#else /* MODULE */ + + + + + /*************************************************************** + * tlan_probe + * + * Returns: + * 0 on success, error code on error + * Parms: + * dev device struct to use if adapter is + * found. + * + * The name is lower case to fit in with all the rest of + * the netcard_probe names. This function looks for a/ + * another TLan based adapter, setting it up with the + * provided device struct if one is found. + * + **************************************************************/ + +extern int tlan_probe( struct device *dev ) +{ + static int pad_allocated = 0; + int found; + TLanPrivateInfo *priv; + u8 bus, dfn, irq, rev; + u32 io_base, dl_ix; + + found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &dl_ix ); + if ( found ) { + dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); + if ( dev->priv == NULL ) { + printk( "TLAN: Could not allocate memory for device.\n" ); + } + memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + priv = (TLanPrivateInfo *) dev->priv; + + dev->name = priv->devName; + strcpy( priv->devName, " " ); + + dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + + dev->base_addr = io_base; + dev->irq = irq; + + priv->pciBus = bus; + priv->pciDeviceFn = dfn; + priv->pciRevision = rev; + priv->pciEntry = dl_ix; + + if ( ! pad_allocated ) { + TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, GFP_KERNEL | GFP_DMA ); + if ( TLanPadBuffer == NULL ) { + printk( "TLAN: Could not allocate memory for pad buffer.\n" ); + } else { + pad_allocated = 1; + memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + } + } + printk("TLAN %d.%d: %s irq=%2d io=%04x, %s\n",TLanVersionMajor, + TLanVersionMinor, + dev->name, + (int) irq, + io_base, + TLanDeviceList[dl_ix].deviceName ); + TLan_Init( dev ); + } + + return ( ( found ) ? 0 : -ENODEV ); + +} /* tlan_probe */ + + +#endif /* MODULE */ + + + + + /*************************************************************** + * TLan_PciProbe + * + * Returns: + * 1 if another TLAN card was found, 0 if not. + * Parms: + * pci_bus The PCI bus the card was found + * on. + * pci_dfn The PCI whatever the card was + * found at. + * pci_irq The IRQ of the found adapter. + * pci_rev The revision of the adapter. + * pci_io_base The first IO port used by the + * adapter. + * dl_ix The index in the device list + * of the adapter. + * + * This function searches for an adapter with PCI vendor + * and device IDs matching those in the TLanDeviceList. + * The function 'remembers' the last device it found, + * and so finds a new device (if anymore are to be found) + * each time the function is called. It then looks up + * pertinent PCI info and returns it to the caller. + * + **************************************************************/ + +int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) +{ + static int dl_index = 0; + static int pci_index = 0; + + int not_found; + u8 pci_latency; + u16 pci_command; + int reg; + + + if ( ! pcibios_present() ) { + printk( "TLAN: PCI Bios not present.\n" ); + return 0; + } + + for (; TLanDeviceList[dl_index].vendorId != 0; dl_index++) { + + not_found = pcibios_find_device( + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId, + pci_index, + pci_bus, + pci_dfn + ); + + if ( ! not_found ) { + + TLAN_DBG( + TLAN_DEBUG_GNRL, + "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", + TLanDeviceList[dl_index].vendorId, + TLanDeviceList[dl_index].deviceId + ); + + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); + pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command); + pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); + pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 0x10) { + pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n"); + } + + for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) { + pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base); + if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { + *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base); + break; + } else { + *pci_io_base = 0; + } + } + + if ( *pci_io_base == 0 ) + printk("TLAN: IO mapping not available, ignoring device.\n"); + + if (pci_command & PCI_COMMAND_MASTER) { + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Bus mastering is active.\n"); + } + + pci_index++; + + if ( *pci_io_base ) { + *dl_ix = dl_index; + return 1; + } + + } else { + pci_index = 0; + } + } + + return 0; + +} /* TLan_PciProbe */ + + + + + /*************************************************************** + * TLan_Init + * + * Returns: + * 0 on success, error code otherwise. + * Parms: + * dev The structure of the device to be + * init'ed. + * + * This function completes the initialization of the + * device structure and driver. It reserves the IO + * addresses, allocates memory for the lists and bounce + * buffers, retrieves the MAC address from the eeprom + * and assignes the device's methods. + * + **************************************************************/ + +int TLan_Init( struct device *dev ) +{ + int dma_size; + int err; + int i; + TLanPrivateInfo *priv; + + priv = (TLanPrivateInfo *) dev->priv; + + err = check_region( dev->base_addr, 0x10 ); + if ( err ) { + printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n", + dev->name, + dev->base_addr, + 0x10 ); + return -EIO; + } + request_region( dev->base_addr, 0x10, TLanSignature ); + + dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) + * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); + priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); + if ( priv->dmaStorage == NULL ) { + printk( "TLAN: Could not allocate lists and buffers for %s.\n", + dev->name ); + return -ENOMEM; + } + memset( priv->dmaStorage, 0, dma_size ); + priv->rxList = (TLanList *) + ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); + priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; + priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); + priv->txBuffer = priv->rxBuffer + + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); + + err = 0; + for ( i = 0; i < 6 ; i++ ) + err |= TLan_EeReadByte( dev->base_addr, + (u8) 0x83 + i, + (u8 *) &dev->dev_addr[i] ); + if ( err ) + printk( "TLAN: %s: Error reading MAC from eeprom: %d\n", + dev->name, + err ); + dev->addr_len = 6; + + dev->open = &TLan_Open; + dev->hard_start_xmit = &TLan_StartTx; + dev->stop = &TLan_Close; + dev->get_stats = &TLan_GetStats; + dev->set_multicast_list = &TLan_SetMulticastList; + +#ifndef MODULE + + aui = dev->mem_start & 0x01; + debug = dev->mem_end; + +#endif /* MODULE */ + + return 0; + +} /* TLan_Init */ + + + + + /*************************************************************** + * TLan_Open + * + * Returns: + * 0 on success, error code otherwise. + * Parms: + * dev Structure of device to be opened. + * + * This routine puts the driver and TLAN adapter in a + * state where it is ready to send and receive packets. + * It allocates the IRQ, resets and brings the adapter + * out of reset, and allows interrupts. It also delays + * the startup for autonegotiation or sends a Rx GO + * command to the adapter, as appropriate. + * + **************************************************************/ + +int TLan_Open( struct device *dev ) +{ + int err; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); + err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); + if ( err ) { + printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); + return -EAGAIN; + } + + MOD_INC_USE_COUNT; + + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* NOTE: It might not be necessary to read the stats before a + reset if you don't care what the values are. + */ + TLan_ResetLists( dev ); + TLan_ReadAndClearStats( dev, TLAN_IGNORE ); + TLan_Reset( dev ); + TLan_Reset( dev ); + TLan_SetMac( dev, 0, dev->dev_addr ); + outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + if ( debug >= 1 ) + outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); + + init_timer( &priv->timer ); + priv->timer.data = (unsigned long) dev; + priv->timer.function = &TLan_Timer; + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s opened. Revision = %x\n", dev->name, priv->tlanRev ); + + return 0; + +} /* TLan_Open */ + + + + + /*************************************************************** + * TLan_StartTx + * + * Returns: + * 0 on success, non-zero on failure. + * Parms: + * skb A pointer to the sk_buff containing the + * frame to be sent. + * dev The device to send the data on. + * + * This function adds a frame to the Tx list to be sent + * ASAP. First it verifies that the adapter is ready and + * there is room in the queue. Then it sets up the next + * available list, copies the frame to the corresponding + * buffer. If the adapter Tx channel is idle, it gives + * the adapter a Tx Go command on the list, otherwise it + * sets the forward address of the previous list to point + * to this one. Then it frees the sk_buff. + * + **************************************************************/ + +int TLan_StartTx( struct sk_buff *skb, struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *tail_list; + u8 *tail_buffer; + int pad; + + if ( ! priv->phyOnline ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name ); + dev_kfree_skb( skb, FREE_WRITE ); + return 0; + } + + tail_list = priv->txList + priv->txTail; + if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail ); + dev->tbusy = 1; + priv->txBusyCount++; + return 1; + } + tail_list->forward = 0; + tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); + memcpy( tail_buffer, skb->data, skb->len ); + pad = TLAN_MIN_FRAME_SIZE - skb->len; + if ( pad > 0 ) { + tail_list->frameSize = (u16) skb->len + pad; + tail_list->buffer[0].count = (u32) skb->len; + tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; + tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); + } else { + tail_list->frameSize = (u16) skb->len; + tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; + tail_list->buffer[1].count = 0; + tail_list->buffer[1].address = 0; + } + // are we transferring? + cli(); + tail_list->cStat = TLAN_CSTAT_READY; + if ( ! priv->txInProgress ) { + priv->txInProgress = 1; + outw( 0x4, dev->base_addr + TLAN_HOST_INT ); + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail ); + outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); + } else { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail ); + if ( priv->txTail == 0 ) + ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); + else + ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); + } + sti(); + priv->txTail++; + if ( priv->txTail >= TLAN_NUM_TX_LISTS ) + priv->txTail = 0; + + dev_kfree_skb( skb, FREE_WRITE ); + + dev->trans_start = jiffies; + return 0; + +} /* TLan_StartTx */ + + + + + /*************************************************************** + * TLan_HandleInterrupt + * + * Returns: + * Nothing + * Parms: + * irq The line on which the interrupt + * occurred. + * dev_id A pointer to the device assigned to + * this irq line. + * regs ??? + * + * This function handles an interrupt generated by its + * assigned TLAN adapter. The function deactivates + * interrupts on its adapter, records the type of + * interrupt, executes the appropriate subhandler, and + * acknowdges the interrupt to the adapter (thus + * re-enabling adapter interrupts. + * + **************************************************************/ + +void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + u32 ack; + struct device *dev; + u32 host_cmd; + u16 host_int; + int type; + + dev = (struct device *) dev_id; + + if ( dev->interrupt ) + printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); + dev->interrupt++; + + cli(); + + host_int = inw( dev->base_addr + TLAN_HOST_INT ); + outw( host_int, dev->base_addr + TLAN_HOST_INT ); // Deactivate Ints + + type = ( host_int & TLAN_HI_IT_MASK ) >> 2; + + ack = TLanIntVector[type]( dev, host_int ); + + sti(); + + if ( ack ) { + host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); + outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); + } + + dev->interrupt--; + +} /* TLan_HandleInterrupts */ + + + + + /*************************************************************** + * TLan_Close + * + * Returns: + * An error code. + * Parms: + * dev The device structure of the device to + * close. + * + * This function shuts down the adapter. It records any + * stats, puts the adapter into reset state, deactivates + * its time as needed, and frees the irq it is using. + * + **************************************************************/ + +int TLan_Close(struct device *dev) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + dev->start = 0; + dev->tbusy = 1; + + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + if ( priv->timerSetAt != 0 ) + del_timer( &priv->timer ); + free_irq( dev->irq, dev ); + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name ); + + MOD_DEC_USE_COUNT; + + return 0; + +} /* TLan_Close */ + + + + + /*************************************************************** + * TLan_GetStats + * + * Returns: + * A pointer to the device's statistics structure. + * Parms: + * dev The device structure to return the + * stats for. + * + * This function updates the devices statistics by reading + * the TLAN chip's onboard registers. Then it returns the + * address of the statistics structure. + * + **************************************************************/ + +struct net_device_stats *TLan_GetStats( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + + /* Should only read stats if open ? */ + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount ); + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount ); + if ( debug & TLAN_DEBUG_GNRL ) { + TLan_PrintDio( dev->base_addr ); + TLan_PhyPrint( dev ); + } + if ( debug & TLAN_DEBUG_LIST ) { + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) + TLan_PrintList( priv->rxList + i, "RX", i ); + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) + TLan_PrintList( priv->txList + i, "TX", i ); + } + + return ( &( (TLanPrivateInfo *) dev->priv )->stats ); + +} /* TLan_GetStats */ + + + + + /*************************************************************** + * TLan_SetMulticastList + * + * Returns: + * Nothing + * Parms: + * dev The device structure to set the + * multicast list for. + * + * This function sets the TLAN adaptor to various receive + * modes. If the IFF_PROMISC flag is set, promiscuous + * mode is acitviated. Otherwise, promiscuous mode is + * turned off. If the IFF_ALLMULTI flag is set, then + * the hash table is set to receive all group addresses. + * Otherwise, the first three multicast addresses are + * stored in AREG_1-3, and the rest are selected via the + * hash table, as necessary. + * + **************************************************************/ + +void TLan_SetMulticastList( struct device *dev ) +{ + struct dev_mc_list *dmi = dev->mc_list; + u32 hash1 = 0; + u32 hash2 = 0; + int i; + u32 offset; + u8 tmp; + + if ( dev->flags & IFF_PROMISC ) { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); + } else { + tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); + if ( dev->flags & IFF_ALLMULTI ) { + for ( i = 0; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); + } else { + for ( i = 0; i < dev->mc_count; i++ ) { + if ( i < 3 ) { + TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); + } else { + offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); + if ( offset < 32 ) + hash1 |= ( 1 << offset ); + else + hash2 |= ( 1 << ( offset - 32 ) ); + } + dmi = dmi->next; + } + for ( ; i < 3; i++ ) + TLan_SetMac( dev, i + 1, NULL ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); + TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); + } + } + +} /* TLan_SetMulticastList */ + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Interrupt Vectors and Table + + Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN + Programmer's Guide" for more informations on handling interrupts + generated by TLAN based adapters. + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_HandleInvalid + * + * Returns: + * 0 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles invalid interrupts. This should + * never happen unless some other adapter is trying to use + * the IRQ line assigned to the device. + * + **************************************************************/ + +u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) +{ + host_int = 0; + // printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); + return 0; + +} /* TLan_HandleInvalid */ + + + + + /*************************************************************** + * TLan_HandleTxEOF + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles Tx EOF interrupts which are raised + * by the adapter when it has completed sending the + * contents of a buffer. If detemines which list/buffer + * was completed and resets it. If the buffer was the last + * in the channel (EOC), then the function checks to see if + * another buffer is ready to send, and if so, sends a Tx + * Go command. Finally, the driver activates/continues the + * activity LED. + * + **************************************************************/ + +u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int eoc = 0; + TLanList *head_list; + u32 ack = 1; + + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); + host_int = 0; + head_list = priv->txList + priv->txHead; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted TX frame.\n" ); + } + // printk( "Ack %d CSTAT=%hx\n", priv->txHead, head_list->cStat ); + +#if LINUX_KERNEL_VERSION > 0x20100 + priv->stats->tx_bytes += head_list->frameSize; +#endif + + head_list->cStat = TLAN_CSTAT_UNUSED; + dev->tbusy = 0; + priv->txHead++; + if ( priv->txHead >= TLAN_NUM_TX_LISTS ) + priv->txHead = 0; + if ( eoc ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("TxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + priv->timerSetAt = jiffies; + // printk("TxEOF continuing timer...\n"); + } + } + + return ack; + +} /* TLan_HandleTxEOF */ + + + + + /*************************************************************** + * TLan_HandleStatOverflow + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles the Statistics Overflow interrupt + * which means that one or more of the TLAN statistics + * registers has reached 1/2 capacity and needs to be read. + * + **************************************************************/ + +u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) +{ + host_int = 0; + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + + return 1; + +} /* TLan_HandleStatOverflow */ + + + + + /*************************************************************** + * TLan_HandleRxEOF + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles the Rx EOF interrupt which + * indicates a frame has been received by the adapter from + * the net and the frame has been transferred to memory. + * The function determines the bounce buffer the frame has + * been loaded into, creates a new sk_buff big enough to + * hold the frame, and sends it to protocol stack. It + * then resets the used buffer and appends it to the end + * of the list. If the frame was the last in the Rx + * channel (EOC), the function restarts the receive channel + * by sending an Rx Go command to the adapter. Then it + * activates/continues the activity LED. + * + **************************************************************/ + +u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 ack = 1; + int eoc = 0; + u8 *head_buffer; + TLanList *head_list; + struct sk_buff *skb; + TLanList *tail_list; + void *t; + + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); + host_int = 0; + head_list = priv->rxList + priv->rxHead; + tail_list = priv->rxList + priv->rxTail; + if ( head_list->cStat & TLAN_CSTAT_EOC ) + eoc = 1; + if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) { + printk( "TLAN: Received interrupt for uncompleted RX frame.\n" ); + } else { + skb = dev_alloc_skb( head_list->frameSize + 7 ); + if ( skb == NULL ) { + printk( "TLAN: Couldn't allocate memory for received data.\n" ); + } else { + head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); + skb->dev = dev; + skb_reserve( skb, 2 ); + t = (void *) skb_put( skb, head_list->frameSize ); + // printk( " %hd %p %p\n", head_list->frameSize, skb->data, t ); + +#if LINUX_KERNEL_VERSION > 0x20100 + priv->stats->rx_bytes += head_list->frameSize; +#endif + + memcpy( t, head_buffer, head_list->frameSize ); + skb->protocol = eth_type_trans( skb, dev ); + netif_rx( skb ); + } + } + head_list->forward = 0; + head_list->frameSize = TLAN_MAX_FRAME_SIZE; + head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + tail_list->forward = virt_to_bus( head_list ); + priv->rxHead++; + if ( priv->rxHead >= TLAN_NUM_RX_LISTS ) + priv->rxHead = 0; + priv->rxTail++; + if ( priv->rxTail >= TLAN_NUM_RX_LISTS ) + priv->rxTail = 0; + if ( eoc ) { + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); + if ( priv->phyFlags & TLAN_PHY_ACTIVITY ) { + if ( priv->timerSetAt == 0 ) { + // printk("RxEOF Starting timer...\n"); + priv->timerSetAt = jiffies; + priv->timer.expires = jiffies + TLAN_TIMER_ACT_DELAY; + priv->timerType = TLAN_TIMER_ACT; + add_timer( &priv->timer ); + } else if ( priv->timerType == TLAN_TIMER_ACT ) { + // printk("RxEOF tarting continuing timer...\n"); + priv->timerSetAt = jiffies; + } + } + dev->last_rx = jiffies; + + return ack; + +} /* TLan_HandleRxEOF */ + + + + + /*************************************************************** + * TLan_HandleDummy + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles the Dummy interrupt, which is + * raised whenever a test interrupt is generated by setting + * the Req_Int bit of HOST_CMD to 1. + * + **************************************************************/ + +u32 TLan_HandleDummy( struct device *dev, u16 host_int ) +{ + host_int = 0; + printk( "TLAN: Dummy interrupt on %s.\n", dev->name ); + return 1; + +} /* TLan_HandleDummy */ + + + + + /*************************************************************** + * TLan_HandleTxEOC + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This driver is structured to determine EOC occurances by + * reading the CSTAT member of the list structure. Tx EOC + * interrupts are disabled via the DIO INTDIS register. + * However, TLAN chips before revision 3.0 didn't have this + * functionality, so process EOC events if this is the + * case. + * + **************************************************************/ + +u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *head_list; + u32 ack = 1; + + host_int = 0; + if ( priv->tlanRev < 0x30 ) { + TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail ); + head_list = priv->txList + priv->txHead; + if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + + return ack; + +} /* TLan_HandleTxEOC */ + + + + + /*************************************************************** + * TLan_HandleStatusCheck + * + * Returns: + * 0 if Adapter check, 1 if Network Status check. + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This function handles Adapter Check/Network Status + * interrupts generated by the adapter. It checks the + * vector in the HOST_INT register to determine if it is + * an Adapter Check interrupt. If so, it resets the + * adapter. Otherwise it clears the status registers + * and services the PHY. + * + **************************************************************/ + +u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) +{ + u32 ack; + u32 error; + u8 net_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + ack = 1; + if ( host_int & TLAN_HI_IV_MASK ) { + error = inl( dev->base_addr + TLAN_CH_PARM ); + printk( "TLAN: Adaptor Check on device %s err = 0x%x\n", dev->name, error ); + TLan_ReadAndClearStats( dev, TLAN_RECORD ); + outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); + TLan_ResetLists( dev ); + TLan_Reset( dev ); + dev->tbusy = 0; + TLan_SetMac( dev, 0, dev->dev_addr ); + if ( priv->timerType == 0 ) { + if ( priv->phyFlags & TLAN_PHY_AUTONEG ) { + priv->timer.expires = jiffies + TLAN_TIMER_LINK_DELAY; + priv->timerSetAt = jiffies; + priv->timerType = TLAN_TIMER_LINK; + add_timer( &priv->timer ); + } else { + //printk( " RX GO---->\n" ); + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + } + } + ack = 0; + } else { + net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); + if ( net_sts ) + TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); + if ( net_sts & TLAN_NET_STS_MIRQ ) { + (*priv->phyService)( dev ); + if (debug) { + TLan_PhyPrint( dev ); + } + } + TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Status Check! %s Net_Sts=%x\n", dev->name, (unsigned) net_sts ); + } + + return ack; + +} /* TLan_HandleStatusCheck */ + + + + + /*************************************************************** + * TLan_HandleRxEOC + * + * Returns: + * 1 + * Parms: + * dev Device assigned the IRQ that was + * raised. + * host_int The contents of the HOST_INT + * port. + * + * This driver is structured to determine EOC occurances by + * reading the CSTAT member of the list structure. Rx EOC + * interrupts are disabled via the DIO INTDIS register. + * However, TLAN chips before revision 3.0 didn't have this + * CSTAT member or a INTDIS register, so if this chip is + * pre-3.0, process EOC interrupts normally. + * + **************************************************************/ + +u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + TLanList *head_list; + u32 ack = 1; + + host_int = 0; + if ( priv->tlanRev < 0x30 ) { + TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail ); + head_list = priv->rxList + priv->rxHead; + outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); + ack |= TLAN_HC_GO | TLAN_HC_RT; + priv->rxEocCount++; + } + + return ack; + +} /* TLan_HandleRxEOC */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Timer Function + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_Timer + * + * Returns: + * Nothing + * Parms: + * data A value given to add timer when + * add_timer was called. + * + * This function handles timed functionality for the + * TLAN driver. The two current timer uses are for + * delaying for autonegotionation and driving the ACT LED. + * - Autonegotiation requires being allowed about + * 2 1/2 seconds before attempting to transmit a + * packet. It would be a very bad thing to hang + * the kernel this long, so the driver doesn't + * allow transmission 'til after this time, for + * certain PHYs. It would be much nicer if all + * PHYs were interrupt-capable like the internal + * PHY. + * - The ACT LED, which shows adapter activity, is + * driven by the driver, and so must be left on + * for a short period to power up the LED so it + * can be seen. This delay can be changed by + * changing the TLAN_TIMER_ACT_DELAY in tlan.h, + * if desired. 10 jiffies produces a slightly + * sluggish response. + * + **************************************************************/ + +void TLan_Timer( unsigned long data ) +{ + struct device *dev = (struct device *) data; + u16 gen_sts; + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + + // printk( "TLAN: %s Entered Timer, type = %d\n", dev->name, priv->timerType ); + + switch ( priv->timerType ) { + case TLAN_TIMER_LINK: + TLan_MiiReadReg( dev->base_addr, priv->phyAddr, MII_GEN_STS, &gen_sts ); + if ( gen_sts & MII_GS_LINK ) { + priv->phyOnline = 1; + outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); + outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = jiffies + ( TLAN_TIMER_LINK_DELAY * 2 ); + add_timer( &priv->timer ); + } + break; + case TLAN_TIMER_ACT: + if ( jiffies - priv->timerSetAt >= TLAN_TIMER_ACT_DELAY ) { + TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); + priv->timerSetAt = 0; + priv->timerType = 0; + } else { + priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; + add_timer( &priv->timer ); + } + break; + default: + break; + } + +} /* TLan_Timer */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Adapter Related Routines + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_ResetLists + * + * Returns: + * Nothing + * Parms: + * dev The device structure with the list + * stuctures to be reset. + * + * This routine sets the variables associated with managing + * the TLAN lists to their initial values. + * + **************************************************************/ + +void TLan_ResetLists( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + TLanList *list; + + priv->txHead = 0; + priv->txTail = 0; + for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { + list = priv->txList + i; + list->cStat = TLAN_CSTAT_UNUSED; + list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[2].count = 0; + list->buffer[2].address = 0; + } + + priv->rxHead = 0; + priv->rxTail = TLAN_NUM_RX_LISTS - 1; + for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { + list = priv->rxList + i; + list->cStat = TLAN_CSTAT_READY; + list->frameSize = TLAN_MAX_FRAME_SIZE; + list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); + list->buffer[1].count = 0; + list->buffer[1].address = 0; + if ( i < TLAN_NUM_RX_LISTS - 1 ) + list->forward = virt_to_bus( list + 1 ); + else + list->forward = 0; + } + +} /* TLan_ResetLists */ + + + + + /*************************************************************** + * TLan_PrintDio + * + * Returns: + * Nothing + * Parms: + * io_base Base IO port of the device of + * which to print DIO registers. + * + * This function prints out all the internal (DIO) + * registers of a TLAN chip. + * + **************************************************************/ + +void TLan_PrintDio( u16 io_base ) +{ + u32 data0, data1; + int i; + + printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base ); + printk( "TLAN: Off. +0 +4\n" ); + for ( i = 0; i < 0x4C; i+= 8 ) { + data0 = TLan_DioRead32( io_base, i ); + data1 = TLan_DioRead32( io_base, i + 0x4 ); + printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 ); + } + +} /* TLan_PrintDio */ + + + + + /*************************************************************** + * TLan_PrintList + * + * Returns: + * Nothing + * Parms: + * list A pointer to the TLanList structure to + * be printed. + * type A string to designate type of list, + * "Rx" or "Tx". + * num The index of the list. + * + * This function prints out the contents of the list + * pointed to by the list parameter. + * + **************************************************************/ + +void TLan_PrintList( TLanList *list, char *type, int num) +{ + int i; + + printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list ); + printk( "TLAN: Forward = 0x%08x\n", list->forward ); + printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat ); + printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize ); + // for ( i = 0; i < 10; i++ ) { + for ( i = 0; i < 2; i++ ) { + printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); + } + +} /* TLan_PrintList */ + + + + + /*************************************************************** + * TLan_ReadAndClearStats + * + * Returns: + * Nothing + * Parms: + * dev Pointer to device structure of adapter + * to which to read stats. + * record Flag indicating whether to add + * + * This functions reads all the internal status registers + * of the TLAN chip, which clears them as a side effect. + * It then either adds the values to the device's status + * struct, or discards them, depending on whether record + * is TLAN_RECORD (!=0) or TLAN_IGNORE (==0). + * + **************************************************************/ + +void TLan_ReadAndClearStats( struct device *dev, int record ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u32 tx_good, tx_under; + u32 rx_good, rx_over; + u32 def_tx, crc, code; + u32 multi_col, single_col; + u32 excess_col, late_col, loss; + + outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + tx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); + rx_good = inb( dev->base_addr + TLAN_DIO_DATA ); + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; + rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); + def_tx = inb( dev->base_addr + TLAN_DIO_DATA ); + def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + code = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + + outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + multi_col = inb( dev->base_addr + TLAN_DIO_DATA ); + multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; + single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; + + outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); + excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); + late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); + loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + + if ( record ) { + priv->stats.rx_packets += rx_good; + priv->stats.rx_errors += rx_over + crc + code; + priv->stats.tx_packets += tx_good; + priv->stats.tx_errors += tx_under + loss; + priv->stats.collisions += multi_col + single_col + excess_col + late_col; + + priv->stats.rx_over_errors += rx_over; + priv->stats.rx_crc_errors += crc; + priv->stats.rx_frame_errors += code; + + priv->stats.tx_aborted_errors += tx_under; + priv->stats.tx_carrier_errors += loss; + } + +} /* TLan_ReadAndClearStats */ + + + + + /*************************************************************** + * TLan_Reset + * + * Returns: + * 0 + * Parms: + * dev Pointer to device structure of adapter + * to be reset. + * + * This function resets the adapter and it's physical + * device. See Chap. 3, pp. 9-10 of the "ThunderLAN + * Programmer's Guide" for details. The routine tries to + * implement what is detailed there, though adjustments + * have been made. + * + **************************************************************/ + +int TLan_Reset( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int i; + u32 addr; + u32 data; + u8 data8; + +// 1. Assert reset bit. + + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_AD_RST; + outl(data, dev->base_addr + TLAN_HOST_CMD); + +// 2. Turn off interrupts. ( Probably isn't necessary ) + + data = inl(dev->base_addr + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outl(data, dev->base_addr + TLAN_HOST_CMD); + +// 3. Clear AREGs and HASHs. + + for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) { + TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); + } + +// 4. Setup NetConfig register. + + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + +// 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. + + outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); + outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); + +// 6. Unreset the MII by setting NMRST (in NetSio) to 1. + + outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); + addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_NMRST, addr ); + +// 7. Setup the remaining registers. + + if ( priv->tlanRev >= 0x30 ) { + data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC; + TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 ); + } + TLan_PhySelect( dev ); + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN; + if ( priv->phyFlags & TLAN_PHY_BIT_RATE ) { + data |= TLAN_NET_CFG_BIT; + if ( aui == 1 ) { + TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a ); + } else { + TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 ); + } + } + if ( priv->phyFlags & TLAN_PHY_INTERNAL ) { + data |= TLAN_NET_CFG_PHY_EN; + } + TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + (*priv->phyCheck)( dev ); + data8 = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; + TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data8 ); + data8 = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; + if ( priv->phyFlags & TLAN_PHY_INTS ) { + data8 |= TLAN_NET_MASK_MASK7; + } + TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data8 ); + TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE ); + + if ( priv->phyFlags & TLAN_PHY_UNMANAGED ) { + priv->phyOnline = 1; + } + + return 0; + +} /* TLan_Reset */ + + + + + /*************************************************************** + * TLan_SetMac + * + * Returns: + * Nothing + * Parms: + * dev Pointer to device structure of adapter + * on which to change the AREG. + * areg The AREG to set the address in (0 - 3). + * mac A pointer to an array of chars. Each + * element stores one byte of the address. + * IE, it isn't in ascii. + * + * This function transfers a MAC address to one of the + * TLAN AREGs (address registers). The TLAN chip locks + * the register on writing to offset 0 and unlocks the + * register after writing to offset 5. If NULL is passed + * in mac, then the AREG is filled with 0's. + * + **************************************************************/ + +void TLan_SetMac( struct device *dev, int areg, char *mac ) +{ + int i; + + areg *= 6; + + if ( mac != NULL ) { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); + } else { + for ( i = 0; i < 6; i++ ) + TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); + } + +} /* TLan_SetMac */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver PHY Layer Routines + + The TLAN chip can drive any number of PHYs (physical devices). Rather + than having lots of 'if' or '#ifdef' statements, I have created a + second driver layer for the PHYs. Each PHY can be identified from its + id in registers 2 and 3, and can be given a Check and Service routine + that will be called when the adapter is reset and when the adapter + receives a Network Status interrupt, respectively. + +****************************************************************************** +*****************************************************************************/ + + +static TLanPhyIdEntry TLanPhyIdTable[] = { + { 0x4000, + 0x5014, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x4000, + 0x5015, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x4000, + 0x5016, + &TLan_PhyInternalCheck, + &TLan_PhyInternalService, + TLAN_PHY_ACTIVITY | TLAN_PHY_INTS | TLAN_PHY_INTERNAL }, + { 0x2000, + 0x5C00, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x2000, + 0x5C01, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_ACTIVITY | TLAN_PHY_AUTONEG }, + { 0x7810, + 0x0000, + &TLan_PhyDp83840aCheck, + &TLan_PhyNop, + TLAN_PHY_AUTONEG }, + { 0x0000, + 0x0000, + NULL, + NULL, + 0 + } + }; + + + /********************************************************************* + * TLan_PhyPrint + * + * Returns: + * Nothing + * Parms: + * dev A pointer to the device structure of the adapter + * which the desired PHY is located. + * + * This function prints the registers a PHY. + * + ********************************************************************/ + +void TLan_PhyPrint( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 i, data0, data1, data2, data3, phy; + u32 io; + + phy = priv->phyAddr; + io = dev->base_addr; + + if ( ( phy > 0 ) && ( phy <= TLAN_PHY_MAX_ADDR ) ) { + printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy ); + printk( "TLAN: Off. +0 +1 +2 +3 \n" ); + for ( i = 0; i < 0x20; i+= 4 ) { + printk( "TLAN: 0x%02x", i ); + TLan_MiiReadReg( io, phy, i, &data0 ); + printk( " 0x%04hx", data0 ); + TLan_MiiReadReg( io, phy, i + 1, &data1 ); + printk( " 0x%04hx", data1 ); + TLan_MiiReadReg( io, phy, i + 2, &data2 ); + printk( " 0x%04hx", data2 ); + TLan_MiiReadReg( io, phy, i + 3, &data3 ); + printk( " 0x%04hx\n", data3 ); + } + } else { + printk( "TLAN: Device %s, PHY 0x%02x (Unmanaged/Unknown).\n", + dev->name, + phy + ); + } + +} /* TLan_PhyPrint */ + + + + + /********************************************************************* + * TLan_PhySelect + * + * Returns: + * Nothing + * Parms: + * dev A pointer to the device structure of the adapter + * for which the PHY needs determined. + * + * This function decides which PHY amoung those attached to the + * TLAN chip is to be used. The TLAN chip can be attached to + * multiple PHYs, and the driver needs to decide which one to + * talk to. Currently this routine picks the PHY with the lowest + * address as the internal PHY address is 0x1F, the highest + * possible. This strategy assumes that there can be only one + * other PHY, and, if it exists, it is the one to be used. If + * token ring PHYs are ever supported, this routine will become + * a little more interesting... + * + ********************************************************************/ + +void TLan_PhySelect( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + int phy; + int entry; + u16 id_hi[TLAN_PHY_MAX_ADDR + 1]; + u16 id_lo[TLAN_PHY_MAX_ADDR + 1]; + u16 hi; + u16 lo; + u16 vendor; + u16 device; + + priv->phyCheck = &TLan_PhyNop; // Make sure these aren't ever NULL + priv->phyService = &TLan_PhyNop; + + vendor = TLanDeviceList[priv->pciEntry].vendorId; + device = TLanDeviceList[priv->pciEntry].deviceId; + + // This is a bit uglier than I'd like, but the 0xF130 device must + // NOT be assigned a valid PHY as it uses an unmanaged, bit-rate + // PHY. It is simplest just to use another goto, rather than + // nesting the two for loops in the if statement. + + if ( ( vendor == PCI_VENDOR_ID_COMPAQ ) && + ( device == PCI_DEVICE_ID_NETFLEX_3P ) ) { + entry = 0; + phy = 0; + goto FINISH; + } + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + hi = lo = 0; + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_HI, &hi ); + TLan_MiiReadReg( dev->base_addr, phy, MII_GEN_ID_LO, &lo ); + id_hi[phy] = hi; + id_lo[phy] = lo; + TLAN_DBG( TLAN_DEBUG_GNRL, + "TLAN: Phy %2x, hi = %hx, lo = %hx\n", + phy, + hi, + lo + ); + } + + for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { + if ( ( aui == 1 ) && ( phy != TLAN_PHY_MAX_ADDR ) ) { + if ( id_hi[phy] != 0xFFFF ) { + TLan_MiiSync(dev->base_addr); + TLan_MiiWriteReg(dev->base_addr, + phy, + MII_GEN_CTL, + MII_GC_PDOWN | + MII_GC_LOOPBK | + MII_GC_ISOLATE ); + + } + continue; + } + for ( entry = 0; TLanPhyIdTable[entry].check; entry++) { + if ( ( id_hi[phy] == TLanPhyIdTable[entry].idHi ) && + ( id_lo[phy] == TLanPhyIdTable[entry].idLo ) ) { + TLAN_DBG( TLAN_DEBUG_GNRL, + "TLAN: Selected Phy %hx\n", + phy + ); + goto FINISH; + } + } + } + + entry = 0; + phy = 0; + +FINISH: + + if ( ( entry == 0 ) && ( phy == 0 ) ) { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLan_PhyNop; + priv->phyService = TLan_PhyNop; + priv->phyFlags = TLAN_PHY_BIT_RATE | + TLAN_PHY_UNMANAGED | + TLAN_PHY_ACTIVITY; + } else { + priv->phyAddr = phy; + priv->phyEntry = entry; + priv->phyCheck = TLanPhyIdTable[entry].check; + priv->phyService = TLanPhyIdTable[entry].service; + priv->phyFlags = TLanPhyIdTable[entry].flags; + } + +} /* TLan_PhySelect */ + + + + + /*************************************************************** + * TLan_PhyNop + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure. + * + * This function does nothing and is meant as a stand-in + * for when a Check or Service function would be + * meaningless. + * + **************************************************************/ + +int TLan_PhyNop( struct device *dev ) +{ + dev = NULL; + return 0; + +} /* TLan_PhyNop */ + + + + + /*************************************************************** + * TLan_PhyInternalCheck + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be checked. + * + * This function resets the internal PHY on a TLAN chip. + * See Chap. 7, "Physical Interface (PHY)" of "ThunderLAN + * Programmer's Guide" + * + **************************************************************/ + +int TLan_PhyInternalCheck( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 gen_ctl; + u32 io; + u16 phy; + u16 value; + u8 sio; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + udelay(50000); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + + udelay(500000); + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + if ( aui ) + value |= TLAN_TC_AUISEL; + else + value &= ~TLAN_TC_AUISEL; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( ( value & MII_GS_LINK ) || aui ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); + + sio = TLan_DioRead8( io, TLAN_NET_SIO ); + sio |= TLAN_NET_SIO_MINTEN; + TLan_DioWrite8( io, TLAN_NET_SIO, sio ); + + return 0; + +} /* TLanPhyInternalCheck */ + + + + + /*************************************************************** + * TLan_PhyInternalService + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be serviced. + * + * This function services an interrupt generated by the + * internal PHY. It can turn on/off the link LED. See + * Chap. 7, "Physical Interface (PHY)" of "ThunderLAN + * Programmer's Guide". + * + **************************************************************/ + +int TLan_PhyInternalService( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 tlphy_sts; + u16 gen_sts; + u16 an_exp; + u32 io; + u16 phy; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, TLAN_TLPHY_STS, &tlphy_sts ); + TLan_MiiReadReg( io, phy, MII_GEN_STS, &gen_sts ); + TLan_MiiReadReg( io, phy, MII_AN_EXP, &an_exp ); + if ( ( gen_sts & MII_GS_LINK ) || aui ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + if ( ( tlphy_sts & TLAN_TS_POLOK ) == 0) { + u16 value; + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value); + value |= TLAN_TC_SWAPOL; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value); + } + + return 0; + +} /* TLan_PhyInternalService */ + + + + + /*************************************************************** + * TLan_PhyDp83840aCheck + * + * Returns: + * Nothing + * Parms: + * dev A pointer to a device structure of the + * adapter holding the PHY to be reset. + * + * This function resets a National Semiconductor DP83840A + * 10/100 Mb/s PHY device. See National Semiconductor's + * data sheet for more info. This PHY is used on Compaq + * Netelligent 10/100 cards. + * + **************************************************************/ + +static int TLan_PhyDp83840aCheck( struct device *dev ) +{ + TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + u16 gen_ctl; + int i; + u32 io; + u16 phy; + u16 value; + u8 sio; + + io = dev->base_addr; + phy = priv->phyAddr; + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &gen_ctl ); + if ( gen_ctl & MII_GC_PDOWN ) { + TLan_MiiSync( io ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK ); + for ( i = 0; i < 500000; i++ ) + SLOW_DOWN_IO; + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_RESET | MII_GC_LOOPBK ); + TLan_MiiSync( io ); + } + + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + while ( value & MII_GC_RESET ) + TLan_MiiReadReg( io, phy, MII_GEN_CTL, &value ); + + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_LOOPBK | MII_GC_DUPLEX ); + // TLan_MiiWriteReg( io, phy, MII_GEN_CTL, MII_GC_DUPLEX ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0 ); + TLan_MiiReadReg( io, phy, MII_AN_ADV, &value ); + value &= ~0x0140; + TLan_MiiWriteReg( io, phy, MII_AN_ADV, value ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1000 ); + TLan_MiiWriteReg( io, phy, MII_GEN_CTL, 0x1200 ); + + for ( i = 0; i < 50000; i++ ) + SLOW_DOWN_IO; + +/* + // Read Possible Latched Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + // Read Real Link Status + TLan_MiiReadReg( io, phy, MII_GEN_STS, &value ); + if ( value & MII_GS_LINK ) { + priv->phyOnline = 1; + TLan_DioWrite8( io, TLAN_LED_REG, TLAN_LED_LINK ); + } else { + priv->phyOnline = 0; + TLan_DioWrite8( io, TLAN_LED_REG, 0 ); + } + + // Enable Interrupts + TLan_MiiReadReg( io, phy, TLAN_TLPHY_CTL, &value ); + value |= TLAN_TC_INTEN; + TLan_MiiWriteReg( io, phy, TLAN_TLPHY_CTL, value ); +*/ + sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); + sio &= ~TLAN_NET_SIO_MINTEN; + TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); +// priv->phyOnline = 1; + + return 0; + +} /* TLan_PhyDp83840aCheck */ + + + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver MII Routines + + These routines are based on the information in Chap. 2 of the + "ThunderLAN Programmer's Guide", pp. 15-24. + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_MiiReadReg + * + * Returns: + * 0 if ack received ok + * 1 otherwise. + * + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be queried. + * reg The register whose contents are to be + * retreived. + * val A pointer to a variable to store the + * retrieved value. + * + * This function uses the TLAN's MII bus to retreive the contents + * of a given register on a PHY. It sends the appropriate info + * and then reads the 16-bit register value from the MII bus via + * the TLAN SIO register. + * + **************************************************************/ + +int TLan_MiiReadReg(u16 base_port, u16 dev, u16 reg, u16 *val) +{ + u8 nack; + u16 sio, tmp; + u32 i; + int err; + int minten; + + err = FALSE; + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync(base_port); + + minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); + if ( minten ) + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x2, 2 ); /* Read ( 10b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */ + + nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */ + if (nack) { /* No ACK, so fake it */ + for (i = 0; i < 16; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + tmp = 0xffff; + err = TRUE; + } else { /* ACK, so read data */ + for (tmp = 0, i = 0x8000; i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) + tmp |= i; + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + } + + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + + if ( minten ) + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); + + *val = tmp; + + sti(); + + return err; + +} /* TLan_MiiReadReg */ + + + + + /*************************************************************** + * TLan_MiiSendData + * + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be queried. + * data The value to be placed on the MII bus. + * num_bits The number of bits in data that are to + * be placed on the MII bus. + * + * This function sends on sequence of bits on the MII + * configuration bus. + * + **************************************************************/ + +void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) +{ + u16 sio; + u32 i; + + if ( num_bits == 0 ) + return; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); + + for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + if ( data & i ) + TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); + } + +} /* TLan_MiiSendData */ + + + + + /*************************************************************** + * TLan_MiiSync + * + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * + * This functions syncs all PHYs in terms of the MII configuration + * bus. + * + **************************************************************/ + +void TLan_MiiSync( u16 base_port ) +{ + int i; + u16 sio; + + outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); + for ( i = 0; i < 32; i++ ) { + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + } + +} /* TLan_MiiSync */ + + + + + /*************************************************************** + * TLan_MiiWriteReg + * + * Returns: + * Nothing + * Parms: + * base_port The base IO port of the adapter in + * question. + * dev The address of the PHY to be written to. + * reg The register whose contents are to be + * written. + * val The value to be written to the register. + * + * This function uses the TLAN's MII bus to write the contents of a + * given register on a PHY. It sends the appropriate info and then + * writes the 16-bit register value from the MII configuration bus + * via the TLAN SIO register. + * + **************************************************************/ + +void TLan_MiiWriteReg(u16 base_port, u16 dev, u16 reg, u16 val) +{ + u16 sio; + int minten; + + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + cli(); + + TLan_MiiSync( base_port ); + + minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); + if ( minten ) + TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); + + TLan_MiiSendData( base_port, 0x1, 2 ); /* Start ( 01b ) */ + TLan_MiiSendData( base_port, 0x1, 2 ); /* Write ( 01b ) */ + TLan_MiiSendData( base_port, dev, 5 ); /* Device # */ + TLan_MiiSendData( base_port, reg, 5 ); /* Register # */ + + TLan_MiiSendData( base_port, 0x2, 2 ); /* Send ACK */ + TLan_MiiSendData( base_port, val, 16 ); /* Send Data */ + + TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */ + TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + + if ( minten ) + TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); + + sti(); + +} /* TLan_MiiWriteReg */ + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Eeprom routines + + The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A + EEPROM. These functions are based on information in Microchip's + data sheet. I don't know how well this functions will work with + other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + + /*************************************************************** + * TLan_EeSendStart + * + * Returns: + * Nothing + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * + * This function sends a start cycle to an EEPROM attached + * to a TLAN chip. + * + **************************************************************/ + +void TLan_EeSendStart( u16 io_base ) +{ + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + +} /* TLan_EeSendStart */ + + + + + /*************************************************************** + * TLan_EeSendByte + * + * Returns: + * If the correct ack was received, 0, otherwise 1 + * Parms: io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * data The 8 bits of information to + * send to the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a + * stop cycle is sent after the + * byte is sent after the ack is + * read. + * + * This function sends a byte on the serial EEPROM line, + * driving the clock to send each bit. The function then + * reverses transmission direction and reads an acknowledge + * bit. + * + **************************************************************/ + +int TLan_EeSendByte( u16 io_base, u8 data, int stop ) +{ + int err; + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + // Assume clock is low, tx is enabled; + for ( place = 0x80; place != 0; place >>= 1 ) { + if ( place & data ) + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + else + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + + if ( ( ! err ) && stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + + return ( err ); + +} /* TLan_EeSendByte */ + + + + + /*************************************************************** + * TLan_EeReceiveByte + * + * Returns: + * Nothing + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * data An address to a char to hold the + * data sent from the EEPROM. + * stop If TLAN_EEPROM_STOP is passed, a + * stop cycle is sent after the + * byte is received, and no ack is + * sent. + * + * This function receives 8 bits of data from the EEPROM + * over the serial link. It then sends and ack bit, or no + * ack and a stop bit. This function is used to retrieve + * data after the address of a byte in the EEPROM has been + * sent. + * + **************************************************************/ + +void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) +{ + u8 place; + u16 sio; + + outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + *data = 0; + + // Assume clock is low, tx is enabled; + TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); + for ( place = 0x80; place; place >>= 1 ) { + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) + *data |= place; + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } + + TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + if ( ! stop ) { + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // Ack = 0 + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + } else { + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); // No ack = 1 (?) + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); // STOP, raise data while clock is high + TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); + TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); + } + +} /* TLan_EeReceiveByte */ + + + + + /*************************************************************** + * TLan_EeReadByte + * + * Returns: + * No error = 0, else, the stage at which the error + * occured. + * Parms: + * io_base The IO port base address for the + * TLAN device with the EEPROM to + * use. + * ee_addr The address of the byte in the + * EEPROM whose contents are to be + * retrieved. + * data An address to a char to hold the + * data obtained from the EEPROM. + * + * This function reads a byte of information from an byte + * cell in the EEPROM. + * + **************************************************************/ + +int TLan_EeReadByte( u16 io_base, u8 ee_addr, u8 *data ) +{ + int err; + + cli(); + + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA0, TLAN_EEPROM_ACK ); + if (err) + return 1; + err = TLan_EeSendByte( io_base, ee_addr, TLAN_EEPROM_ACK ); + if (err) + return 2; + TLan_EeSendStart( io_base ); + err = TLan_EeSendByte( io_base, 0xA1, TLAN_EEPROM_ACK ); + if (err) + return 3; + TLan_EeReceiveByte( io_base, data, TLAN_EEPROM_STOP ); + + sti(); + + return 0; + +} /* TLan_EeReadByte */ + + + + + diff -u --recursive --new-file v2.0.33/linux/drivers/net/tlan.h linux/drivers/net/tlan.h --- v2.0.33/linux/drivers/net/tlan.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/tlan.h Wed Jun 3 15:17:47 1998 @@ -0,0 +1,507 @@ +#ifndef TLAN_H +#define TLAN_H +/******************************************************************** + * + * Linux ThunderLAN Driver + * + * tlan.h + * by James Banks, james.banks@caldera.com + * + * (C) 1997 Caldera, Inc. + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4, colums>=132 + * + ********************************************************************/ + + +#include +#include +#include + +#if LINUX_VERSION_CODE <= 0x20100 +#define net_device_stats enet_statistics +#endif + + + + + /***************************************************************** + * TLan Definitions + * + ****************************************************************/ + +#define FALSE 0 +#define TRUE 1 + +#define TLAN_MIN_FRAME_SIZE 64 +#define TLAN_MAX_FRAME_SIZE 1600 + +#define TLAN_NUM_RX_LISTS 4 +#define TLAN_NUM_TX_LISTS 8 + +#define TLAN_IGNORE 0 +#define TLAN_RECORD 1 + +#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args ); +#define TLAN_DEBUG_GNRL 0x0001 +#define TLAN_DEBUG_TX 0x0002 +#define TLAN_DEBUG_RX 0x0004 +#define TLAN_DEBUG_LIST 0x0008 + + + + + /***************************************************************** + * Device Identification Definitions + * + ****************************************************************/ + + /* NOTE: These have been moved to pci.h, will use them + eventually */ +#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34 +#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32 +#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35 +#define PCI_DEVICE_ID_NETFLEX_3P 0xF130 +#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40 +#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011 + + +typedef struct tlan_pci_id { + u16 vendorId; + u16 deviceId; + char *deviceName; +} TLanPciId; + + + + + /***************************************************************** + * Rx/Tx List Definitions + * + ****************************************************************/ + +#define TLAN_BUFFERS_PER_LIST 10 +#define TLAN_LAST_BUFFER 0x80000000 +#define TLAN_CSTAT_UNUSED 0x8000 +#define TLAN_CSTAT_FRM_CMP 0x4000 +#define TLAN_CSTAT_READY 0x3000 +#define TLAN_CSTAT_EOC 0x0800 +#define TLAN_CSTAT_RX_ERROR 0x0400 +#define TLAN_CSTAT_PASS_CRC 0x0200 +#define TLAN_CSTAT_DP_PR 0x0100 + + +typedef struct tlan_buffer_ref_tag { + u32 count; + u32 address; +} TLanBufferRef; + + +typedef struct tlan_list_tag { + u32 forward; + u16 cStat; + u16 frameSize; + TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST]; +} TLanList; + + +typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + + + + + /***************************************************************** + * PHY definitions + * + ****************************************************************/ + +#define TLAN_PHY_MAX_ADDR 0x1F + +#define TLAN_PHY_ACTIVITY 0x00000001 +#define TLAN_PHY_AUTONEG 0x00000002 +#define TLAN_PHY_INTS 0x00000004 +#define TLAN_PHY_BIT_RATE 0x00000008 +#define TLAN_PHY_UNMANAGED 0x00000010 +#define TLAN_PHY_INTERNAL 0x00000020 + + +typedef int (TLanPhyFunc)( struct device * ); + + +typedef struct tlan_phy_id_entry_tag { + u16 idHi; + u16 idLo; + TLanPhyFunc *check; + TLanPhyFunc *service; + u32 flags; +} TLanPhyIdEntry; + + + + + /***************************************************************** + * TLAN Private Information Structure + * + ****************************************************************/ + +typedef struct tlan_private_tag { + struct device *nextDevice; + void *dmaStorage; + u8 *padBuffer; + TLanList *rxList; + u8 *rxBuffer; + u32 rxHead; + u32 rxTail; + u32 rxEocCount; + TLanList *txList; + u8 *txBuffer; + u32 txHead; + u32 txInProgress; + u32 txTail; + u32 txBusyCount; + u32 phyAddr; + u32 phyEntry; + u32 phyOnline; + u32 phyFlags; + TLanPhyFunc *phyCheck; + TLanPhyFunc *phyService; + u32 timerSetAt; + u32 timerType; + struct timer_list timer; + struct net_device_stats stats; + u32 pciEntry; + u8 pciRevision; + u8 pciBus; + u8 pciDeviceFn; + u8 tlanRev; + char devName[8]; +} TLanPrivateInfo; + + + + + /***************************************************************** + * TLan Driver Timer Definitions + * + ****************************************************************/ + +#define TLAN_TIMER_LINK 1 +#define TLAN_TIMER_ACT 2 + +#define TLAN_TIMER_LINK_DELAY 230 +#define TLAN_TIMER_ACT_DELAY 10 + + + + + /***************************************************************** + * TLan Driver Eeprom Definitions + * + ****************************************************************/ + +#define TLAN_EEPROM_ACK 0 +#define TLAN_EEPROM_STOP 1 + + + + + /***************************************************************** + * Host Register Offsets and Contents + * + ****************************************************************/ + +#define TLAN_HOST_CMD 0x00 +#define TLAN_HC_GO 0x80000000 +#define TLAN_HC_STOP 0x40000000 +#define TLAN_HC_ACK 0x20000000 +#define TLAN_HC_CS_MASK 0x1FE00000 +#define TLAN_HC_EOC 0x00100000 +#define TLAN_HC_RT 0x00080000 +#define TLAN_HC_NES 0x00040000 +#define TLAN_HC_AD_RST 0x00008000 +#define TLAN_HC_LD_TMR 0x00004000 +#define TLAN_HC_LD_THR 0x00002000 +#define TLAN_HC_REQ_INT 0x00001000 +#define TLAN_HC_INT_OFF 0x00000800 +#define TLAN_HC_INT_ON 0x00000400 +#define TLAN_HC_AC_MASK 0x000000FF +#define TLAN_CH_PARM 0x04 +#define TLAN_DIO_ADR 0x08 +#define TLAN_DA_ADR_INC 0x8000 +#define TLAN_DA_RAM_ADR 0x4000 +#define TLAN_HOST_INT 0x0A +#define TLAN_HI_IV_MASK 0x1FE0 +#define TLAN_HI_IT_MASK 0x001C +#define TLAN_DIO_DATA 0x0C + + +/* ThunderLAN Internal Register DIO Offsets */ + +#define TLAN_NET_CMD 0x00 +#define TLAN_NET_CMD_NRESET 0x80 +#define TLAN_NET_CMD_NWRAP 0x40 +#define TLAN_NET_CMD_CSF 0x20 +#define TLAN_NET_CMD_CAF 0x10 +#define TLAN_NET_CMD_NOBRX 0x08 +#define TLAN_NET_CMD_DUPLEX 0x04 +#define TLAN_NET_CMD_TRFRAM 0x02 +#define TLAN_NET_CMD_TXPACE 0x01 +#define TLAN_NET_SIO 0x01 +#define TLAN_NET_SIO_MINTEN 0x80 +#define TLAN_NET_SIO_ECLOK 0x40 +#define TLAN_NET_SIO_ETXEN 0x20 +#define TLAN_NET_SIO_EDATA 0x10 +#define TLAN_NET_SIO_NMRST 0x08 +#define TLAN_NET_SIO_MCLK 0x04 +#define TLAN_NET_SIO_MTXEN 0x02 +#define TLAN_NET_SIO_MDATA 0x01 +#define TLAN_NET_STS 0x02 +#define TLAN_NET_STS_MIRQ 0x80 +#define TLAN_NET_STS_HBEAT 0x40 +#define TLAN_NET_STS_TXSTOP 0x20 +#define TLAN_NET_STS_RXSTOP 0x10 +#define TLAN_NET_STS_RSRVD 0x0F +#define TLAN_NET_MASK 0x03 +#define TLAN_NET_MASK_MASK7 0x80 +#define TLAN_NET_MASK_MASK6 0x40 +#define TLAN_NET_MASK_MASK5 0x20 +#define TLAN_NET_MASK_MASK4 0x10 +#define TLAN_NET_MASK_RSRVD 0x0F +#define TLAN_NET_CONFIG 0x04 +#define TLAN_NET_CFG_RCLK 0x8000 +#define TLAN_NET_CFG_TCLK 0x4000 +#define TLAN_NET_CFG_BIT 0x2000 +#define TLAN_NET_CFG_RXCRC 0x1000 +#define TLAN_NET_CFG_PEF 0x0800 +#define TLAN_NET_CFG_1FRAG 0x0400 +#define TLAN_NET_CFG_1CHAN 0x0200 +#define TLAN_NET_CFG_MTEST 0x0100 +#define TLAN_NET_CFG_PHY_EN 0x0080 +#define TLAN_NET_CFG_MSMASK 0x007F +#define TLAN_MAN_TEST 0x06 +#define TLAN_DEF_VENDOR_ID 0x08 +#define TLAN_DEF_DEVICE_ID 0x0A +#define TLAN_DEF_REVISION 0x0C +#define TLAN_DEF_SUBCLASS 0x0D +#define TLAN_DEF_MIN_LAT 0x0E +#define TLAN_DEF_MAX_LAT 0x0F +#define TLAN_AREG_0 0x10 +#define TLAN_AREG_1 0x16 +#define TLAN_AREG_2 0x1C +#define TLAN_AREG_3 0x22 +#define TLAN_HASH_1 0x28 +#define TLAN_HASH_2 0x2C +#define TLAN_GOOD_TX_FRMS 0x30 +#define TLAN_TX_UNDERUNS 0x33 +#define TLAN_GOOD_RX_FRMS 0x34 +#define TLAN_RX_OVERRUNS 0x37 +#define TLAN_DEFERRED_TX 0x38 +#define TLAN_CRC_ERRORS 0x3A +#define TLAN_CODE_ERRORS 0x3B +#define TLAN_MULTICOL_FRMS 0x3C +#define TLAN_SINGLECOL_FRMS 0x3E +#define TLAN_EXCESSCOL_FRMS 0x40 +#define TLAN_LATE_COLS 0x41 +#define TLAN_CARRIER_LOSS 0x42 +#define TLAN_ACOMMIT 0x43 +#define TLAN_LED_REG 0x44 +#define TLAN_LED_ACT 0x10 +#define TLAN_LED_LINK 0x01 +#define TLAN_BSIZE_REG 0x45 +#define TLAN_MAX_RX 0x46 +#define TLAN_INT_DIS 0x48 +#define TLAN_ID_TX_EOC 0x04 +#define TLAN_ID_RX_EOF 0x02 +#define TLAN_ID_RX_EOC 0x01 + + + +/* ThunderLAN Interrupt Codes */ + +#define TLAN_INT_NUMBER_OF_INTS 8 + +#define TLAN_INT_NONE 0x0000 +#define TLAN_INT_TX_EOF 0x0001 +#define TLAN_INT_STAT_OVERFLOW 0x0002 +#define TLAN_INT_RX_EOF 0x0003 +#define TLAN_INT_DUMMY 0x0004 +#define TLAN_INT_TX_EOC 0x0005 +#define TLAN_INT_STATUS_CHECK 0x0006 +#define TLAN_INT_RX_EOC 0x0007 + + + +/* ThunderLAN MII Registers */ + +/* Generic MII/PHY Registers */ + +#define MII_GEN_CTL 0x00 +#define MII_GC_RESET 0x8000 +#define MII_GC_LOOPBK 0x4000 +#define MII_GC_SPEEDSEL 0x2000 +#define MII_GC_AUTOENB 0x1000 +#define MII_GC_PDOWN 0x0800 +#define MII_GC_ISOLATE 0x0400 +#define MII_GC_AUTORSRT 0x0200 +#define MII_GC_DUPLEX 0x0100 +#define MII_GC_COLTEST 0x0080 +#define MII_GC_RESERVED 0x007F +#define MII_GEN_STS 0x01 +#define MII_GS_100BT4 0x8000 +#define MII_GS_100BTXFD 0x4000 +#define MII_GS_100BTXHD 0x2000 +#define MII_GS_10BTFD 0x1000 +#define MII_GS_10BTHD 0x0800 +#define MII_GS_RESERVED 0x07C0 +#define MII_GS_AUTOCMPLT 0x0020 +#define MII_GS_RFLT 0x0010 +#define MII_GS_AUTONEG 0x0008 +#define MII_GS_LINK 0x0004 +#define MII_GS_JABBER 0x0002 +#define MII_GS_EXTCAP 0x0001 +#define MII_GEN_ID_HI 0x02 +#define MII_GEN_ID_LO 0x03 +#define MII_GIL_OUI 0xFC00 +#define MII_GIL_MODEL 0x03F0 +#define MII_GIL_REVISION 0x000F +#define MII_AN_ADV 0x04 +#define MII_AN_LPA 0x05 +#define MII_AN_EXP 0x06 + +/* ThunderLAN Specific MII/PHY Registers */ + +#define TLAN_TLPHY_ID 0x10 +#define TLAN_TLPHY_CTL 0x11 +#define TLAN_TC_IGLINK 0x8000 +#define TLAN_TC_SWAPOL 0x4000 +#define TLAN_TC_AUISEL 0x2000 +#define TLAN_TC_SQEEN 0x1000 +#define TLAN_TC_MTEST 0x0800 +#define TLAN_TC_RESERVED 0x07F8 +#define TLAN_TC_NFEW 0x0004 +#define TLAN_TC_INTEN 0x0002 +#define TLAN_TC_TINT 0x0001 +#define TLAN_TLPHY_STS 0x12 +#define TLAN_TS_MINT 0x8000 +#define TLAN_TS_PHOK 0x4000 +#define TLAN_TS_POLOK 0x2000 +#define TLAN_TS_TPENERGY 0x1000 +#define TLAN_TS_RESERVED 0x0FFF + + + +/* Routines to access internal registers. */ + +inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3))); + +} /* TLan_DioRead8 */ + + + + +inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2))); + +} /* TLan_DioRead16 */ + + + + +inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inl(base_addr + TLAN_DIO_DATA)); + +} /* TLan_DioRead32 */ + + + + +inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3)); + +} + + + + +inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_ClearBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) & ~bit, port); +} + + + + +inline int TLan_GetBit(u8 bit, u16 port) +{ + return ((int) (inb_p(port) & bit)); +} + + + + +inline void TLan_SetBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) | bit, port); +} + + +inline u32 xor( u32 a, u32 b ) +{ + return ( ( a && ! b ) || ( ! a && b ) ); +} +#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) ) +#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) ) + +inline u32 TLan_HashFunc( u8 *a ) +{ + u32 hash; + + hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) ); + hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1; + hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2; + hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3; + hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4; + hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5; + + return hash; + +} + + + + +#endif diff -u --recursive --new-file v2.0.33/linux/drivers/net/tulip.c linux/drivers/net/tulip.c --- v2.0.33/linux/drivers/net/tulip.c Sun Sep 7 12:28:36 1997 +++ linux/drivers/net/tulip.c Wed Jun 3 15:17:47 1998 @@ -1,7 +1,6 @@ /* tulip.c: A DEC 21040-family ethernet driver for linux. */ /* - NOTICE: THIS IS THE ALPHA TEST VERSION! - Written 1994-1997 by Donald Becker. + Written 1994-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. @@ -17,10 +16,20 @@ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html */ -static const char *version = "tulip.c:v0.79 9/3/97 becker@cesdis.gsfc.nasa.gov\n"; +#define SMP_CHECK +static const char version[] = "tulip.c:v0.88 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 8 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; +static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ + /* Set if the PCI BIOS detects the chips on a multiport board backwards. */ #ifdef REVERSE_PROBE_ORDER static int reverse_probe = 1; @@ -33,10 +42,14 @@ bonding and packet priority. There are no ill effects from too-large receive rings. */ #define TX_RING_SIZE 16 -#define RX_RING_SIZE 16 +#define RX_RING_SIZE 32 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ -static const rx_copybreak = 200; +#ifdef __alpha__ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif /* The following example shows how to always use the 10base2 port. */ #ifdef notdef @@ -125,6 +138,7 @@ #endif #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 @@ -156,7 +170,7 @@ 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 and 21142. These chips are used on +are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on many PCI boards including the SMC EtherPower series. @@ -251,30 +265,61 @@ #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 +#define PCI_DEVICE_ID_PNIC 0x0002 +#define PCI_DEVICE_ID_PNIC_X 0x0168 + +#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 + /* The rest of these values should never change. */ 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}; static struct tulip_chip_table { - int device_id; - char *chip_name; - int flags; - void (*media_timer)(unsigned long data); + int 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_DEVICE_ID_DEC_TULIP, "DS21040 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/ - { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */ + { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 128, 0x0001ebef, + 0, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 128, 0x0001ebef, + HAS_MEDIA_TABLE, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE, tulip_timer }, + { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 256, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, + { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 256, 0x0001ebef, + 0, pnic_timer }, + { PCI_DEVICE_ID_MX98713, "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE, tulip_timer /* Tulip-like! */ }, + { PCI_DEVICE_ID_MX98715, "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { PCI_DEVICE_ID_MX98725, "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, {0, 0, 0, 0}, }; /* This matches the table above. */ -enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, }; +enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725}; static const char * const medianame[] = { "10baseT", "10base2", "AUI", "100baseTx", @@ -283,15 +328,17 @@ "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", }; /* A full-duplex map for above. */ -static const char media_fd[] = -{0,0,0,0, 0xff,0xff,0,0, 0xff,0,0xff,0x01, 0,0,0xff,0 }; +enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; /* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; -static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, }; -static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, }; +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; /* Offsets to the Command and Status Registers, "CSRs". All accesses @@ -304,9 +351,9 @@ /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + NormalIntr=0x10000, AbnormalIntr=0x8000, RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, - TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, - TxIntr=0x01, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, }; /* The Tulip Rx and Tx buffer descriptors. */ @@ -339,7 +386,6 @@ struct mediainfo *next; int info_type; int index; - struct non_mii { char media; unsigned char csr12val; char bitnum, flags;} non_mii; unsigned char *info; }; @@ -354,7 +400,7 @@ /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; char *rx_buffs; /* Address of temporary Rx buffers. */ - int setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ int chip_id; int revision; #if LINUX_VERSION_CODE > 0x20139 @@ -363,32 +409,37 @@ struct enet_statistics stats; #endif struct timer_list timer; /* Media selection timer. */ + int interrupt; /* In-interrupt flag. */ +#ifdef SMP_CHECK + int smp_proc_id; /* Which processor in IRQ handler. */ +#endif unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int full_duplex_lock:1; 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. */ unsigned int mediasense:1; /* Media sensing in progress. */ unsigned int csr6; /* Current CSR6 control settings. */ unsigned char eeprom[128]; /* Serial EEPROM contents. */ - signed char phys[4]; /* MII device addresses. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ struct mediatable *mtable; int cur_index; /* Current media index. */ - unsigned char pci_bus, pci_device_fn; + unsigned char pci_bus, pci_dev_fn; int pad0, pad1; /* Used for 8-byte alignment */ }; -/* Used to pass the full-duplex flag, etc. */ -static int full_duplex[8] = {0, }; -static int options[8] = {0, }; - -static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, int ioaddr, int chip_id, int options); static void parse_eeprom(struct device *dev); static int read_eeprom(int ioaddr, int location); static int mdio_read(int ioaddr, int phy_id, int location); +static void mdio_write(int ioaddr, int phy_id, int location, int value); static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); static void tulip_timer(unsigned long data); @@ -399,18 +450,19 @@ static void tulip_interrupt IRQ(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); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); +#endif #ifdef NEW_MULTICAST -static void set_multicast_list(struct device *dev); +static void set_rx_mode(struct device *dev); #else -static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); #endif -#ifdef MODULE /* A list of all installed Tulip devices, for removing the driver module. */ static struct device *root_tulip_dev = NULL; -#endif /* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, but now receives directly into full-sized skbuffs that are allocated @@ -432,9 +484,9 @@ unsigned char pci_bus, pci_device_fn; for (;pci_index < 0xff; pci_index++) { - unsigned char pci_irq_line, pci_latency; - unsigned short pci_command, vendor, device; - unsigned int pci_ioaddr, chip_idx = 0; + u16 vendor, device, pci_command, new_command; + u32 pci_ioaddr; + int chip_idx = 0; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, @@ -448,64 +500,71 @@ PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - 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 (vendor != PCI_VENDOR_ID_DEC) + if (vendor != PCI_VENDOR_ID_DEC + && vendor != PCI_VENDOR_ID_LITEON + && vendor != PCI_VENDOR_ID_MXIC) continue; + if (vendor == PCI_VENDOR_ID_LITEON) + device = PCI_DEVICE_ID_PNIC_X; + else if (vendor == PCI_VENDOR_ID_MXIC) + device = PCI_DEVICE_ID_MX98713; for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) if (device == tulip_tbl[chip_idx].device_id) break; if (tulip_tbl[chip_idx].chip_name == 0) { - printk(KERN_INFO "Unknown Digital PCI ethernet chip type" - " %4.4x"" detected: not configured.\n", device); + printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" + " %4.4x %4.4x"" detected: not configured.\n", + vendor, device); continue; } if (tulip_debug > 2) - printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n", - pci_ioaddr, pci_irq_line); + printk(KERN_DEBUG "Found %s at I/O %#x.\n", + tulip_tbl[chip_idx].chip_name, pci_ioaddr); - if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE)) + if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) continue; -#ifdef MODULE - dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, - cards_found); -#else - dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1); -#endif + 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); + } + dev = tulip_probe1(pci_bus, pci_device_fn, dev, + pci_ioaddr, chip_idx, cards_found); + + /* Get and check the bus-master and latency values. */ if (dev) { - /* Get and check the bus-master and latency values. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk(KERN_INFO " PCI Master Bit has not been set! Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, pci_command); - } - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 10) { - printk(KERN_INFO " 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 (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x.\n", - pci_latency); - /* 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++; + unsigned char pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(KERN_INFO " 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 (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++; } } } @@ -513,7 +572,8 @@ return cards_found ? 0 : -ENODEV; } -static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, +static struct device *tulip_probe1(int pci_bus, int pci_device_fn, + struct device *dev, int ioaddr, int chip_id, int board_idx) { static int did_version = 0; /* Already printed version info. */ @@ -521,6 +581,7 @@ /* 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; + u8 pci_irq_line; int i; unsigned short sum; @@ -529,13 +590,15 @@ dev = init_etherdev(dev, 0); - printk(KERN_INFO "%s: DEC %s at %#3x,", + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + printk(KERN_INFO "%s: %s at %#3x,", dev->name, tulip_tbl[chip_id].chip_name, ioaddr); /* Stop the chip's Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* Clear the missed-packet counter. */ - (volatile)inl(ioaddr + CSR8); + (volatile int)inl(ioaddr + CSR8); if (chip_id == DC21041) { if (inl(ioaddr + CSR9) & 0x8000) { @@ -561,6 +624,16 @@ dev->dev_addr[i] = value; sum += value & 0xff; } + } else if (chip_id == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + ((u16*)dev->dev_addr)[i] = value; + sum += value & 0xffff; + } } else { /* Must be a new chip, with a serial EEPROM interface. */ /* We read the whole EEPROM, and sort it out later. DEC has a specification _Digital Semiconductor 21X4 Serial ROM Format_ @@ -581,6 +654,13 @@ sum += ee_data[i + sa_offset]; } } + /* Lite-On boards have the address byte-swapped. */ + if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00) + for (i = 0; i < 6; i+=2) { + char tmp = dev->dev_addr[i]; + dev->dev_addr[i] = dev->dev_addr[i+1]; + dev->dev_addr[i+1] = tmp; + } /* On the Zynx 315 Etherarray and other multiport boards only the first Tulip has an EEPROM. The addresses of the subsequent ports are derived from the first. @@ -591,33 +671,37 @@ for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; - irq = last_irq; +#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ + pci_irq_line = last_irq; +#endif } for (i = 0; i < 6; i++) printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); - printk(", IRQ %d.\n", irq); - last_irq = irq; + printk(", IRQ %d.\n", pci_irq_line); + last_irq = pci_irq_line; /* 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, tulip_tbl[chip_id].chip_name); dev->base_addr = ioaddr; - dev->irq = irq; + dev->irq = pci_irq_line; /* Make certain the data structures are quadword aligned. */ tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7); memset(tp, 0, sizeof(*tp)); dev->priv = tp; -#ifdef MODULE tp->next_module = root_tulip_dev; root_tulip_dev = dev; -#endif + tp->pci_bus = pci_bus; + tp->pci_dev_fn = pci_device_fn; tp->chip_id = chip_id; #ifdef TULIP_FULL_DUPLEX tp->full_duplex = 1; + tp->full_duplex_lock = 1; #endif #ifdef TULIP_DEFAULT_MEDIA tp->default_port = TULIP_DEFAULT_MEDIA; @@ -627,32 +711,64 @@ #endif /* The lower four bits are the media type. */ - if (board_idx >= 0) { - tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0; + if (board_idx >= 0 && board_idx < MAX_UNITS) { tp->default_port = options[board_idx] & 15; - if (tp->default_port) - tp->medialock = 1; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; + if (mtu[board_idx] > 0) + dev->mtu = mtu[board_idx]; + } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + tp->medialock = 1; + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; } + if (tp->full_duplex) + tp->full_duplex_lock = 1; /* This is logically part of probe1(), but too complex to write inline. */ - if (chip_id != DC21040) + if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) parse_eeprom(dev); - if (tp->mtable && tp->mtable->has_mii) { + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = 0x03e1; + + if ((tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { int phy, phy_idx; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes much time. */ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(ioaddr, phy, 0); - if (mii_status != 0xffff && mii_status != 0x0000) { - tp->phys[phy_idx++] = phy; - printk(KERN_INFO "%s: MII transceiver found at MDIO address %d.\n", - dev->name, phy); + int mii_status = mdio_read(ioaddr, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + int mii_reg0 = mdio_read(ioaddr, phy, 0); + 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_DEBUG "%s: Advertising %4.4x on PHY %d," + " previously advertising %4.4x.\n", + dev->name, reg4, phy, mdio_read(ioaddr, phy, 4)); + mdio_write(ioaddr, phy, 4, reg4); + } + /* Enable autonegotiation: some boards default to off. */ + mdio_write(ioaddr, phy, 0, mii_reg0 | + (tp->full_duplex ? 0x1100 : 0x1000) | + (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); } } - if (phy_idx == 0) { + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", dev->name); tp->phys[0] = 1; @@ -664,8 +780,11 @@ dev->hard_start_xmit = &tulip_start_xmit; dev->stop = &tulip_close; dev->get_stats = &tulip_get_stats; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &private_ioctl; +#endif #ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_multicast_list; + dev->set_multicast_list = &set_rx_mode; #endif /* Reset the xcvr interface and turn on heartbeat. */ @@ -674,17 +793,36 @@ outl(0x00000000, ioaddr + CSR13); outl(0xFFFFFFFF, ioaddr + CSR14); outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ - outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6); + outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6); outl(0x0000EF05, ioaddr + CSR13); break; - case DC21140: case DC21142: - if (tp->mtable) - outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); - break; case DC21040: outl(0x00000000, ioaddr + CSR13); outl(0x00000004, ioaddr + CSR13); break; + case DC21140: default: + if (tp->mtable) + 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. */ + break; + case LC82C168: + outl(0x00420000, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + break; + case MX98713: case MX98715: case MX98725: + outl(0x00000000, ioaddr + CSR6); + outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + outl(0x00000001, ioaddr + CSR13); + break; } return dev; @@ -722,49 +860,50 @@ {0, 0, 0, 0, {}}}; static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", - "21142 non-MII PHY", "21142 MII PHY", }; + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; #define EEPROM_SIZE 128 +#if defined(__i386__) +#define get_u16(ptr) (*(u16 *)(ptr)) +#else +#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) +#endif + static void parse_eeprom(struct device *dev) { /* The last media info list parsed, for multiport boards. */ static struct mediatable *last_mediatable = NULL; static unsigned char *last_ee_data = NULL; - static controller_index = 0; + static int controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; int ioaddr = dev->base_addr; unsigned char *ee_data = tp->eeprom; int i; - { - static int done_did_that = 0; - if (done_did_that++ == 0) - printk(KERN_INFO" The following verbose information is emitted for\n" - KERN_INFO" bug reports on media selection.\n"); - } tp->mtable = 0; for (i = 0; i < EEPROM_SIZE/2; i++) - ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); /* Detect an old-style (SA only) EEPROM layout: memcmp(eedata, eedata+16, 8). */ for (i = 0; i < 8; i ++) - if (ee_data[i] != ee_data[16+i]) - break; + if (ee_data[i] != ee_data[16+i]) + break; if (i >= 8) { - if (ee_data[0] == 0xff) { - if (last_mediatable) { - controller_index++; - printk(KERN_INFO "%s: Controller %d of multiport board.\n", - dev->name, controller_index); - tp->mtable = last_mediatable; - ee_data = last_ee_data; - goto subsequent_board; - } else - printk(KERN_INFO "%s: Missing EEPROM, this device may not work correctly!\n", + if (ee_data[0] == 0xff) { + if (last_mediatable) { + controller_index++; + printk(KERN_INFO "%s: Controller %d of multiport board.\n", + dev->name, controller_index); + tp->mtable = last_mediatable; + ee_data = last_ee_data; + goto subsequent_board; + } else + printk(KERN_INFO "%s: Missing EEPROM, this interface may " + "not work correctly!\n", dev->name); - return; - } + return; + } /* Do a fix-up based on the vendor half of the station address prefix. */ for (i = 0; eeprom_fixups[i].name; i++) { if (dev->dev_addr[0] == eeprom_fixups[i].addr0 @@ -787,7 +926,7 @@ } } if (tulip_debug > 1) { - printk(KERN_DEBUG "\nread_eeprom:"); + printk(KERN_DEBUG "read_eeprom:"); for (i = 0; i < 64; i++) { printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ", read_eeprom(ioaddr, i)); @@ -801,30 +940,44 @@ } subsequent_board: - if (tp->chip_id == DC21041) { + if (ee_data[27] == 0) { /* No valid media table. */ + } else if (tp->chip_id == DC21041) { unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; - short media = *(u16 *)p; - int count = p[2]; + short media; + int count; + + media = get_u16(p); + p += 2; + count = *p++; printk(KERN_INFO "%s:21041 Media information at %d, default media " "%4.4x (%s).\n", dev->name, ee_data[27], media, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { - unsigned char media_code = p[3 + i*7]; - unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1]; - printk(KERN_INFO "%s: 21041 media %2.2x (%s)," - " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", - dev->name, media_code & 15, medianame[media_code & 15], - csrvals[0], csrvals[1], csrvals[2]); + unsigned char media_code = *p++; + u16 csrvals[3]; + for (i = 0; i < 3; i++) { + csrvals[i] = get_u16(p); + p += 2; + } + if (media_code & 0x40) { + printk(KERN_INFO "%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } else + printk(KERN_INFO "%s: 21041 media #%d, %s.\n", + dev->name, media_code & 15, medianame[media_code & 15]); } } else { unsigned char *p = (void *)ee_data + ee_data[27]; unsigned char csr12dir = 0; int count; struct mediatable *mtable; - short media = *((u16 *)p)++; + u16 media = get_u16(p); - if (tp->chip_id == DC21140) + p += 2; + if (tp->chip_id == DC21140 || tp->chip_id == MX98713) csr12dir = *p++; count = *p++; mtable = (struct mediatable *) @@ -847,6 +1000,8 @@ leaf->type = 0; leaf->media = p[0] & 0x3f; leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; p += 4; } else { leaf->type = p[1]; @@ -864,16 +1019,7 @@ "sequences %d/%d long, capabilities %2.2x %2.2x.\n", dev->name, bp[0], bp[1], bp[1 + bp[1]*2], bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); - if (tulip_debug > 2) { - int mii_reg; - printk(KERN_DEBUG "%s: MII xcvr control registers:", - dev->name); - for (mii_reg = 0; mii_reg < 32; mii_reg++) - printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg)); - printk(".\n"); - } } - printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " "by a %s (%d) block.\n", dev->name, i, medianame[leaf->media], leaf->media, @@ -941,6 +1087,16 @@ return retval; } +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* 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 or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + /* Read and write the MII registers using software-generated serial MDIO protocol. It is just different enough from the EEPROM protocol to not share code. The maxium data clock rate is 2.5 Mhz. */ @@ -950,17 +1106,12 @@ #define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ #define MDIO_ENB_IN 0x40000 #define MDIO_DATA_READ 0x80000 -#ifdef _LINUX_DELAY_H -#define mdio_delay() udelay(1) -#else -#define mdio_delay() __SLOW_DOWN_IO -#endif static int mdio_read(int ioaddr, int phy_id, int location) { int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - unsigned short retval = 0; + int retval = 0; int mdio_addr = ioaddr + CSR9; /* Establish sync by sending at least 32 logic ones. */ @@ -971,35 +1122,54 @@ mdio_delay(); } /* Shift the read command bits out. */ - for (i = 17; i >= 0; i--) { + for (i = 15; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(dataval, mdio_addr); + outl(MDIO_ENB | dataval, mdio_addr); mdio_delay(); - outl(dataval | MDIO_SHIFT_CLK, mdio_addr); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); - outl(dataval, mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); } - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN, mdio_addr); + return (retval>>1) & 0xffff; +} - for (i = 16; i > 0; i--) { - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); +static void mdio_write(int ioaddr, int phy_id, int location, int value) +{ + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int mdio_addr = ioaddr + CSR9; + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outl(MDIO_ENB_IN, mdio_addr); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); } - /* Clear out extra bits. */ - for (i = 16; i > 0; i--) { - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { outl(MDIO_ENB_IN, mdio_addr); mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); } - return retval; + return; } @@ -1011,7 +1181,7 @@ int i = 0; /* On some chip revs we must set the MII/SYM port before the reset!? */ - if (tp->mtable && tp->mtable->has_mii) + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) outl(0x00040000, ioaddr + CSR6); /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ @@ -1033,23 +1203,30 @@ Tx and Rx queues and the address filter list. */ #if defined(__alpha__) /* ToDo: Alpha setting could be better. */ - outl(0x00200000 | 0xE000, ioaddr + CSR0); -#else + outl(0x01A00000 | 0xE000, ioaddr + CSR0); +#elif defined(__powerpc__) + outl(0x01A00080 | 0x8000, ioaddr + CSR0); +#elif defined(__i386__) #if defined(MODULE) /* When a module we don't have 'x86' to check. */ - outl(0x00200000 | 0x4800, ioaddr + CSR0); + outl(0x01A00000 | 0x4800, ioaddr + CSR0); #else - outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); +#if (LINUX_VERSION_CODE > 0x2014c) +#define x86 boot_cpu_data.x86 +#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, - 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000)); + 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, - tulip_tbl[tp->chip_id].chip_name, dev)) { + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } #else @@ -1073,7 +1250,7 @@ /* Fill the whole address filter table with our physical address. */ { u16 *eaddrs = (u16 *)dev->dev_addr; - int *setup_frm = tp->setup_frame, i; + u32 *setup_frm = tp->setup_frame, i; /* You must add the broadcast address when doing perfect filtering! */ *setup_frm++ = 0xffff; @@ -1097,7 +1274,7 @@ outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); if (dev->if_port == 0) - dev->if_port = tp->default_port; + dev->if_port = tp->default_port; if (tp->chip_id == DC21041 && dev->if_port > 4) /* Invalid: Select initial TP, autosense, autonegotiate. */ dev->if_port = 4; @@ -1105,14 +1282,16 @@ /* Allow selecting a default media. */ if (tp->mtable == NULL) goto media_picked; - if (dev->if_port) + if (dev->if_port) { + int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : + (dev->if_port == 12 ? 0 : dev->if_port); for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == - (dev->if_port == 12 ? 0 : dev->if_port)) { + if (tp->mtable->mleaf[i].media == looking_for) { printk(KERN_INFO "%s: Using user-specified media %s.\n", dev->name, medianame[dev->if_port]); goto media_picked; } + } if ((tp->mtable->defaultmedia & 0x0800) == 0) for (i = 0; i < tp->mtable->leafcount; i++) if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { @@ -1120,26 +1299,33 @@ dev->name, medianame[tp->mtable->mleaf[i].media]); goto media_picked; } + /* Start sensing first non-full-duplex media. */ for (i = tp->mtable->leafcount - 1; - (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--) + (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) ; media_picked: - tp->cur_index = i; tp->csr6 = 0; - select_media(dev, 1); + 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); + } else + select_media(dev, 1); /* Start the chip's Tx to process setup frame. */ outl(tp->csr6, ioaddr + CSR6); outl(tp->csr6 | 0x2000, ioaddr + CSR6); dev->tbusy = 0; - dev->interrupt = 0; + tp->interrupt = 0; dev->start = 1; - /* Enable interrupts by setting the interrupt mask. */ - outl(0x0001fbff, ioaddr + CSR7); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); outl(tp->csr6 | 0x2002, ioaddr + CSR6); outl(0, ioaddr + CSR2); /* Rx poll demand */ @@ -1151,9 +1337,9 @@ /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&tp->timer); - tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.expires = RUN_AT(5*HZ); tp->timer.data = (unsigned long)dev; - tp->timer.function = &tulip_timer; /* timer handler */ + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; add_timer(&tp->timer); return 0; @@ -1174,8 +1360,8 @@ switch (mleaf->type) { case 0: /* 21140 non-MII xcvr. */ if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control" - " setting %2.2x.\n", + printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" + " with control setting %2.2x.\n", dev->name, p[1]); dev->if_port = p[0]; if (startup) @@ -1183,26 +1369,11 @@ outl(p[1], ioaddr + CSR12); new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); break; - case 1: - if (startup) { - outl(mtable->csr12dir | 0x100, ioaddr + CSR12); - dev->if_port = 11; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a reset sequence of length %d.\n", - dev->name, p[2 + p[1]]); - for (i = 0; i < p[2 + p[1]]; i++) - outl(p[3 + p[1] + i], ioaddr + CSR12); - if (tulip_debug > 2) - printk(KERN_DEBUG "%s Doing a transceiver setup sequence of length %d.\n", - dev->name, p[1]); - for (i = 0; i < p[1]; i++) - outl(p[2 + i], ioaddr + CSR12); - } - check_mii = 1; - new_csr6 = 0x020C0000; - break; case 2: case 4: { - u16 *setup = (u16*)&p[1]; + u16 setup[3]; + for (i = 0; i < 3; i++) + setup[i] = get_u16(&p[i*2 + 1]); + 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", @@ -1212,56 +1383,74 @@ outl(setup[1], ioaddr + CSR14); outl(setup[2], ioaddr + CSR15); outl(setup[0], ioaddr + CSR13); - setup += 3; - } else { + for (i = 0; i < 3; i++) /* Re-fill setup[] */ + setup[i] = get_u16(&p[i*2 + 7]); + } else if (dev->if_port <= 4) { outl(0, ioaddr + CSR13); outl(t21142_csr14[dev->if_port], ioaddr + CSR14); outl(t21142_csr15[dev->if_port], ioaddr + CSR15); outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else { + outl(0, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(0, ioaddr + CSR13); } outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ outl(setup[1]<<16, ioaddr + CSR15); /* Data */ - new_csr6 = 0x02000000; + if (mleaf->type == 4) + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; break; } - case 3: { + case 1: case 3: { + int phy_num = p[0]; int init_length = p[1]; - u16 * init_sequence = (u16*)(p + 2); - int reset_length = p[2 + init_length*2]; - u16 * reset_sequence = (u16*)&p[3 + init_length*2]; + u16 *misc_info; + u16 to_advertise; dev->if_port = 11; - if (startup) { - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a 21142 reset sequence of length %d.\n", - dev->name, reset_length); - for (i = 0; i < reset_length; i++) - outl(reset_sequence[i] << 16, ioaddr + CSR15); - } - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n", - dev->name, init_length); - for (i = 0; i < init_length; i++) - outl(init_sequence[i] << 16, ioaddr + CSR15); check_mii = 1; - new_csr6 = 0x020C0000; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) + for (i = 0; i < reset_length; i++) + outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + for (i = 0; i < init_length; i++) + outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i], ioaddr + CSR12); + } + for (i = 0; i < init_length; i++) + outl(init_sequence[i], ioaddr + CSR12); + } + to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; + tp->advertising[phy_num] = to_advertise; + if (tulip_debug > 1 || 1) + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", + dev->name, to_advertise, phy_num, tp->phys[phy_num]); + /* Bogus: put in by a committee? */ + mdio_write(ioaddr, tp->phys[phy_num], 4, to_advertise); break; } default: - new_csr6 = 0x020C0000; + new_csr6 = 0x020E0000; } if (tulip_debug > 1) printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", dev->name, medianame[dev->if_port], inl(ioaddr + CSR12) & 0xff); - } else if (tp->chip_id == DC21140) { - /* Set media type to MII @ 100mbps: 0x020C0000 */ - new_csr6 = 0x020C0000; - dev->if_port = 11; - if (tulip_debug > 1) { - printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n", - dev->name, inl(ioaddr + CSR12) & 0xff); - } } else if (tp->chip_id == DC21041) { if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", @@ -1272,7 +1461,33 @@ outl(t21041_csr15[dev->if_port], ioaddr + CSR15); outl(t21041_csr13[dev->if_port], ioaddr + CSR13); new_csr6 = 0x80020000; - } else { /* 21040 */ + } else if (tp->chip_id == LC82C168) { + if (startup) + dev->if_port = 0; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x," + " media %s.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), + medianame[dev->if_port]); + if (startup) { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } else if (dev->if_port == 3 || dev->if_port == 5) { + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + if (startup) + outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */ + else + outl(0x1F868, ioaddr + 0xB8); + } else { + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + } else if (tp->chip_id == DC21040) { /* 21040 */ /* Turn on the xcvr interface. */ int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) @@ -1283,6 +1498,23 @@ outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + if (tp->mii_cnt) { + dev->if_port = 11; + } else + dev->if_port = 3; + if (media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No media description table, assuming " + "%s transceiver, CSR12 %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12)); } tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); @@ -1357,125 +1589,262 @@ break; } break; - case DC21140: case DC21142: { + case DC21140: case DC21142: case MX98713: default: { struct medialeaf *mleaf; unsigned char *p; if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ - /* Assume this is like a SMC card, and check its link beat bit. */ - if ((dev->if_port == 0 && (csr12 & 0x0080)) || - (dev->if_port == 1 && (csr12 & 0x0040) == 0)) { - dev->if_port ^= 1; - /* Stop the transmit process. */ - tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000); - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to" - " %s media.\n", dev->name, - csr12 & 0xff, - dev->if_port ? "100baseTx" : "10baseT"); - outl(tp->csr6 | 0xA002, ioaddr + CSR6); - dev->trans_start = jiffies; - next_tick = (24*HZ)/10; - } else { - next_tick = 10*HZ; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link" - " beat detected as %s.\n", dev->name, - csr12 & 0xff, - dev->if_port ? "100baseTx" : "10baseT"); + /* Not much that can be done. + Assume this a generic MII or SYM transceiver. */ + next_tick = 60*HZ; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " + "CSR12 0x%2.2x.\n", + dev->name, inl(ioaddr + CSR6), csr12 & 0xff); + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ + int offset = mleaf->type == 4 ? 5 : 2; + s8 bitnum = p[offset]; + if (p[offset+1] & 0x80) { + if (tulip_debug > 1) + printk(KERN_DEBUG"%s: Transceiver monitor tick " + "CSR12=%#2.2x, no media sense.\n", + dev->name, csr12); + if (mleaf->type == 4) { + if (mleaf->media == 3 && (csr12 & 0x02)) + goto select_next_media; + } + break; + } + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" + " bit %d is %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ + goto actually_mii; + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_cap[dev->if_port] & MediaIsFD) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: { /* 21140, 21142 MII */ + int mii_reg1, mii_reg5; + actually_mii: + mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1); + mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x, CSR12 %2.2x, %cD.\n", + dev->name, mii_reg1, mii_reg5, csr12, + tp->full_duplex ? 'F' : 'H'); + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(ioaddr, 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); +#ifdef notyet + goto select_next_media; +#endif } + if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) + ; /* No MII device or no link partner report */ + else 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) /* Gurppp, should be >1 */ + 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; break; } - mleaf = &tp->mtable->mleaf[tp->cur_index]; - p = mleaf->leafdata; - switch (mleaf->type) { - case 0: case 4: { - /* Type 0 non-MII or #4 SYM transceiver. Check the link beat bit. */ - s8 bitnum = p[mleaf->type == 4 ? 5 : 2]; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is" - " %d, expecting %d.\n", - dev->name, csr12, (bitnum >> 1) & 7, - (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, - (bitnum >= 0)); - /* Check that the specified bit has the proper value. */ - if ((bitnum < 0) != - ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, - medianame[mleaf->media]); - break; - } - if (tp->medialock) + case 2: /* 21142 serial block has no link beat. */ + default: break; - select_next_media: - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - dev->if_port = tp->mtable->mleaf[tp->cur_index].media; - if (media_fd[dev->if_port]) - goto select_next_media; /* Skip FD entries. */ - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: No link beat on media %s," - " trying transceiver type %s.\n", - dev->name, medianame[mleaf->media & 15], - medianame[tp->mtable->mleaf[tp->cur_index].media]); - select_media(dev, 0); - /* Restart the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - next_tick = (24*HZ)/10; - break; - } - case 1: - { - int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); - printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, " - "Link partner report %4.4x.\n", - dev->name, csr12, mii_reg5); - if (mii_reg5 != 0xffff - && mdio_read(ioaddr, tp->phys[0], 1) & 0x0020) { - int full_duplex = mii_reg5 & 0x0100 ? 1 : 0; - if (full_duplex != tp->full_duplex) { - tp->full_duplex = full_duplex; - tp->csr6 ^= 0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - } - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Setting %s-duplex based on MII" - " Xcvr #%d partner capability of %4.4x.\n", - dev->name, full_duplex ? "full" : "half", - tp->phys[0], mii_reg5); - } - } + } + } + break; + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} - /* Hack for D-Link: Full duplex indication is on bit 3. */ - if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0x80 - && dev->dev_addr[2] == 0xC8) { - if (csr12 & 0x08) { - tp->full_duplex = 0; - tp->csr6 &= ~0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - } else { - tp->full_duplex = 1; - tp->csr6 |= 0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - } - } - break; - case 2: /* 21142 non-MII */ - case 3: /* 21142 MII */ - next_tick = (24*HZ)/10; - break; - default: - break; - } +/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list + of available transceivers. */ +static void t21142_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = 0; + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + dev->name, csr12, medianame[dev->if_port]); + if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + new_csr6 = 0x82420200; + outl(new_csr6, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + } + } 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", + dev->name, csr12); + if (!(csr12 & 4)) { /* 10mbps link beat good. */ + new_csr6 = 0x82420000; + dev->if_port = 0; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else if (csr12 & 0x100) { + new_csr6 = 0x82420200; + dev->if_port = 2; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + } else { + /* Select 100mbps port to check for link beat. */ + new_csr6 = 0x83860000; + dev->if_port = 3; + outl(0, ioaddr + CSR13); + outl(0x0003FF7F, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(1, ioaddr + CSR13); + } + if (tulip_debug > 1) + printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + dev->name, medianame[dev->if_port]); + if (new_csr6 != (tp->csr6 & ~0x00D5)) { + tp->csr6 &= 0x00D5; + tp->csr6 |= new_csr6; + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } } - default: /* Invalid chip type. */ - break; + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void t21142_lnk_change( struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n", + dev->name, csr12, inl(ioaddr + CSR5)); + + if ((csr12 & 0x7000) == 0x5000) { + if (csr12 & 0x01800000) { + /* Switch to 100mbps mode. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + if (csr12 & 0x01000000) { + dev->if_port = 5; + tp->csr6 = 0x83860200; + } else { + dev->if_port = 3; + tp->csr6 = 0x83860000; + } + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } /* 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", + 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", + dev->name); + } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ + printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + dev->name); + dev->if_port = 0; + } else { /* 100mbps link beat good. */ + printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n", + dev->name); + dev->if_port = 3; + tp->csr6 = 0x83860000; + outl(0x0003FF7F, ioaddr + CSR14); + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } +} + + +static void mxic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) { + printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, + inl(ioaddr + CSR12)); } if (next_tick) { tp->timer.expires = RUN_AT(next_tick); @@ -1483,6 +1852,59 @@ } } +static void pnic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int phy_reg = inl(ioaddr + 0xB8); + int next_tick = 60*HZ; + int duplex; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, inl(ioaddr + CSR5)); + if (tp->full_duplex_lock) + return; /* Do not bother to set timer. */ + duplex = phy_reg & 0x30000000 ? 1 : 0; + 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) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Setting %s-duplex based on" + " PNIC PHY report of %8.8x.\n", + dev->name, tp->full_duplex ? "full" : "half", phy_reg); + } + if (phy_reg & 0x04000000) { /* Remote link fault */ + /*outl(0x0201F078, ioaddr + 0xB8);*/ + next_tick = 3*HZ; + } + if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " + "CSR5 %8.8x, PHY %3.3x.\n", + dev->name, medianame[dev->if_port], csr12, + inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); + if (dev->if_port == 0) { + dev->if_port = 3; + } else + dev->if_port = 0; + next_tick = 3*HZ; + select_media(dev, 0); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + static void tulip_tx_timeout(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; @@ -1502,20 +1924,6 @@ } dev->trans_start = jiffies; return; - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) { - /* Stop the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - dev->if_port ^= 1; - 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)); - 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 if (tp->chip_id == DC21041) { u32 csr12 = inl(ioaddr + CSR12); @@ -1535,6 +1943,27 @@ tp->stats.tx_errors++; dev->trans_start = jiffies; 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", @@ -1616,15 +2045,6 @@ int entry; u32 flag; -#ifndef final_version - if (skb == NULL || skb->len <= 0) { - printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - dev_tint(dev); - return 0; - } -#endif - /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { @@ -1681,20 +2101,31 @@ struct device *dev = (struct device *)(irq2dev_map[irq]); #endif - struct tulip_private *lp; - int csr5, ioaddr, boguscnt = 12; + struct tulip_private *tp; + int csr5, ioaddr, work_budget = max_interrupt_work; if (dev == NULL) { - printk ("tulip_interrupt(): irq %d for unknown device.\n", irq); + printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", + irq); return; } ioaddr = dev->base_addr; - lp = (struct tulip_private *)dev->priv; - if (dev->interrupt) + tp = (struct tulip_private *)dev->priv; + if (test_and_set_bit(0, (void*)&tp->interrupt)) { +#ifdef SMP_CHECK + 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()); +#else printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); - + return; +#endif + } dev->interrupt = 1; +#ifdef SMP_CHECK + tp->smp_proc_id = hard_smp_processor_id(); +#endif do { csr5 = inl(ioaddr + CSR5); @@ -1705,23 +2136,24 @@ printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", dev->name, csr5, inl(dev->base_addr + CSR5)); - if ((csr5 & 0x00018000) == 0) + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) break; - if (csr5 & 0x0040) /* Rx interrupt */ - tulip_rx(dev); + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= tulip_rx(dev); - if (csr5 & 0x0007) { /* Tx-done interrupt */ - int dirty_tx; + if (csr5 & (TxNoBuf | TxDied | TxIntr)) { + unsigned int dirty_tx; - for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; - int status = lp->tx_ring[entry].status; + int status = tp->tx_ring[entry].status; if (status < 0) break; /* It still hasn't been Txed */ /* Check for Rx filter setup frames. */ - if (lp->tx_skbuff[entry] == NULL) + if (tp->tx_skbuff[entry] == NULL) continue; if (status & 0x8000) { @@ -1731,74 +2163,96 @@ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", dev->name, status); #endif - lp->stats.tx_errors++; - if (status & 0x4104) lp->stats.tx_aborted_errors++; - if (status & 0x0C00) lp->stats.tx_carrier_errors++; - if (status & 0x0200) lp->stats.tx_window_errors++; - if (status & 0x0002) lp->stats.tx_fifo_errors++; - if ((status & 0x0080) && lp->full_duplex == 0) - lp->stats.tx_heartbeat_errors++; + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + tp->stats.tx_heartbeat_errors++; #ifdef ETHER_STATS - if (status & 0x0100) lp->stats.collisions16++; + if (status & 0x0100) tp->stats.collisions16++; #endif } else { #ifdef ETHER_STATS - if (status & 0x0001) lp->stats.tx_deferred++; + if (status & 0x0001) tp->stats.tx_deferred++; +#endif +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; #endif - lp->stats.collisions += (status >> 3) & 15; - lp->stats.tx_packets++; + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; } /* Free the original skb. */ - dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); - lp->tx_skbuff[entry] = 0; +#if (LINUX_VERSION_CODE > 0x20155) + dev_kfree_skb(tp->tx_skbuff[entry]); +#else + dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); +#endif + tp->tx_skbuff[entry] = 0; } #ifndef final_version - if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); dirty_tx += TX_RING_SIZE; } #endif - if (lp->tx_full && dev->tbusy - && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + if (tp->tx_full && dev->tbusy + && tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) { /* The ring is no longer full, clear tbusy. */ - lp->tx_full = 0; + tp->tx_full = 0; dev->tbusy = 0; mark_bh(NET_BH); } - lp->dirty_tx = dirty_tx; + tp->dirty_tx = dirty_tx; } /* Log errors. */ - if (csr5 & 0x8000) { /* Abnormal error summary bit. */ - if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */ - if (csr5 & 0x0020) { /* Tx FIFO underflow. */ - lp->csr6 |= 0x00200000; /* Reconfigure to store-n-forward. */ - /* Restart the transmit process. */ - outl(lp->csr6 | 0x0002, ioaddr + CSR6); - outl(lp->csr6 | 0x2002, ioaddr + CSR6); - } - if (csr5 & 0x0100) { /* Missed a Rx frame. */ - lp->stats.rx_errors++; - lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 & TxJabber) tp->stats.tx_errors++; + if (csr5 & TxFIFOUnderflow) { + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + if (csr5 & RxDied) { /* Missed a Rx frame. */ + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; } - if (csr5 & 0x0800) { + if (csr5 & TimerInt) { printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n", dev->name, csr5); /* Hmmmmm, it's not clear what to do here. */ } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000) + && tp->chip_id == DC21142) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", + dev->name, csr5); + t21142_lnk_change(dev); + } /* Clear all error sources, included undocumented ones! */ - outl(0x000f7ba, ioaddr + CSR5); + outl(0x0800f7ba, ioaddr + CSR5); } - if (--boguscnt < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n", - dev->name, csr5); - /* Clear all interrupt sources. */ - outl(0x0001ffff, ioaddr + CSR5); + if (--work_budget < 0) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Too much work at interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); +#ifdef notdef + /* Clear all but standard interrupt sources. */ + outl((~csr5) & 0x0001ebef, ioaddr + CSR7); +#endif break; } } while (1); @@ -1807,6 +2261,7 @@ printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); +#ifdef notdef /* Code that should never be run! Perhaps remove after testing.. */ { static int stopit = 10; @@ -1822,95 +2277,84 @@ #endif } } +#endif dev->interrupt = 0; + clear_bit(0, (void*)&tp->interrupt); return; } static int tulip_rx(struct device *dev) { - struct tulip_private *lp = (struct tulip_private *)dev->priv; - int entry = lp->cur_rx % RX_RING_SIZE; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; if (tulip_debug > 4) printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, - lp->rx_ring[entry].status); + tp->rx_ring[entry].status); /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].status >= 0) { - int status = lp->rx_ring[entry].status; + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + if (--rx_work_limit < 0) + break; if ((status & 0x0300) != 0x0300) { if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %8.8x!\n", dev->name, status); - lp->stats.rx_length_errors++; + 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. */ - lp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) lp->stats.rx_length_errors++; - if (status & 0x0004) lp->stats.rx_frame_errors++; - if (status & 0x0002) lp->stats.rx_crc_errors++; - if (status & 0x0001) lp->stats.rx_fifo_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 { - /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ - short pkt_len = (lp->rx_ring[entry].status >> 16) - 4; + short pkt_len = (status >> 16) - 4; 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; - - /* Get a fresh skbuff to replace the filled one. */ - newskb = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (newskb == NULL) { - skb = 0; /* No memory, drop the packet. */ - goto memory_squeeze; - } - /* Pass up the skb already on the Rx ring. */ - skb = lp->rx_skbuff[entry]; - temp = skb_put(skb, pkt_len); - if (bus_to_virt(lp->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(lp->rx_ring[entry].buffer1), - skb->head, temp); - rx_in_place = 1; - lp->rx_skbuff[entry] = newskb; - newskb->dev = dev; - /* Longword alignment required: do not skb_reserve(2)! */ - lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail); - } else - skb = DEV_ALLOC_SKB(pkt_len + 2); - memory_squeeze: - if (skb == NULL) { - int i; - printk(KERN_WARNING "%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++. */ - for (i = 0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) - break; - - if (i > RX_RING_SIZE -2) { - lp->stats.rx_dropped++; - lp->rx_ring[entry].status = 0x80000000; - lp->cur_rx++; - } - break; - } - skb->dev = dev; - if (! rx_in_place) { - skb_reserve(skb, 2); /* 16 byte align the data fields */ + if (pkt_len < rx_copybreak + && (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(lp->rx_ring[entry].buffer1), pkt_len); + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); +#else +#warning Code untested + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); +#endif + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + skb = tp->rx_skbuff[entry]; + 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); +#endif } #if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); @@ -1918,14 +2362,35 @@ skb->len = pkt_len; #endif netif_rx(skb); - lp->stats.rx_packets++; + dev->last_rx = jiffies; + tp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.rx_bytes += pkt_len; +#endif } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } - lp->rx_ring[entry].status = 0x80000000; - entry = (++lp->cur_rx) % RX_RING_SIZE; + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + 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); + 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; } - return 0; + return work_done; } static int @@ -1972,12 +2437,20 @@ #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 } } 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 tp->tx_skbuff[i] = 0; } @@ -1999,6 +2472,71 @@ return &tp->stats; } +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + if (tp->mtable && tp->mtable->has_mii) + data[0] = phy; + else if (tp->chip_id == DC21142) + data[0] = 32; + else + return -ENODEV; + return 0; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + if (data[0] == 32) { /* 21142 pseudo-MII */ + int csr12 = inl(ioaddr + CSR12); + int csr14 = inl(ioaddr + CSR14); + switch (data[1]) { + case 0: { + data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000); + break; } + case 1: + data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + + (csr12&0x06 ? 0x04 : 0); + break; + case 4: { + int csr14 = inl(ioaddr + CSR14); + data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1; + break; + } + case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + default: data[3] = 0; break; + } + } else { + save_flags(flags); + cli(); + data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + } + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + if (data[0] == 32) { /* 21142 pseudo-MII */ + } else { + save_flags(flags); + cli(); + mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + } + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} +#endif /* HAVE_PRIVATE_IOCTL */ + /* 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 @@ -2025,12 +2563,10 @@ return crc; } - -static void #ifdef NEW_MULTICAST -set_multicast_list(struct device *dev) +static void set_rx_mode(struct device *dev) #else -static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) #endif { int ioaddr = dev->base_addr; @@ -2057,13 +2593,13 @@ 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); + 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! - This code may require tweaking for non-x86 architectures! */ + 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; @@ -2090,9 +2626,9 @@ } eaddrs = (u16 *)dev->dev_addr; do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; } while (++i < 15); /* Now add this frame to the Tx list. */ if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { @@ -2105,19 +2641,19 @@ entry = tp->cur_tx++ % TX_RING_SIZE; if (entry != 0) { - /* 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; - tp->tx_ring[entry].buffer1 = 0; - tp->tx_ring[entry].status = 0x80000000; - entry = tp->cur_tx++ % TX_RING_SIZE; + /* 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; + tp->tx_ring[entry].buffer1 = 0; + tp->tx_ring[entry].status = 0x80000000; + entry = tp->cur_tx++ % TX_RING_SIZE; } 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 |= 0x02000000; /* 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; @@ -2133,13 +2669,69 @@ } } +#ifdef CARDBUS + +#include + +static dev_node_t *tulip_attach(dev_locator_t *loc) +{ + u16 dev_id; + u32 io; + u8 bus, devfn; + struct device *dev; + + 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, io, DC21142, -1); + if (dev) { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + strcpy(node->dev_name, dev->name); + node->major = node->minor = 0; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } + return NULL; +} + +static void tulip_detach(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_detach(%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) { + unregister_netdev(*devp); + kfree(*devp); + *devp = *next; + kfree(node); + MOD_DEC_USE_COUNT; + } +} + +struct driver_operations tulip_ops = { + "tulip_cb", tulip_attach, NULL, NULL, 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(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"); #endif /* An additional parameter that may be passed in... */ @@ -2151,8 +2743,12 @@ if (debug >= 0) tulip_debug = debug; - root_tulip_dev = NULL; +#ifdef CARDBUS + register_driver(&tulip_ops); + return 0; +#else return tulip_probe(NULL); +#endif } void @@ -2160,6 +2756,10 @@ { struct device *next_dev; +#ifdef CARDBUS + unregister_driver(&tulip_ops); +#endif + /* 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; @@ -2174,7 +2774,8 @@ /* * Local variables: - * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c" + * 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`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --recursive --new-file v2.0.33/linux/drivers/net/yellowfin.c linux/drivers/net/yellowfin.c --- v2.0.33/linux/drivers/net/yellowfin.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/yellowfin.c Wed Jun 3 15:17:47 1998 @@ -0,0 +1,1302 @@ +/* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */ +/* + Written 1997-1998 by Donald Becker. + + 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 Packet Engines G-NIC PCI Gigabit Ethernet adapter. + It also supports the Symbios Logic version of the same chip core. + + The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + Center of Excellence in Space Data and Information Sciences + Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + + Support and updates available at + http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html +*/ + +static const char *version = "yellowfin.c:v0.99A 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; + +/* A few user-configurable values. */ + +static int max_interrupt_work = 20; +static int min_pci_latency = 64; +static int mtu = 0; +#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ +/* System-wide count of bogus-rx frames. */ +static int bogus_rx = 0; +static int dma_ctrl = 0x004A0263; /* Constrained by errata */ +static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ +#elif YF_NEW /* A future perfect board :->. */ +static int dma_ctrl = 0x00CAC277; /* Override when loading module! */ +static int fifo_cfg = 0x0028; +#else +static int dma_ctrl = 0x004A0263; /* Constrained by errata */ +static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ +#endif + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. */ +static const rx_copybreak = 100; + +/* Keep the ring sizes a power of two for efficiency. + Making the Tx ring too large decreases the effectiveness of channel + bonding and packet priority. + There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((2000*HZ)/1000) + +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Processor type for cache alignment. */ +#include +#include + +#include +#include +#include + +/* Kernel compatibility defines, 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) +#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 */ + +#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) +#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +static const char *card_name = "Yellowfin G-NIC Gbit Ethernet"; + +/* The PCI I/O space extent. */ +#define YELLOWFIN_TOTAL_SIZE 0x100 + +#ifdef HAVE_DEVLIST +struct netdev_entry yellowfin_drv = +{card_name, yellowfin_pci_probe, YELLOWFIN_TOTAL_SIZE, NULL}; +#endif + +#ifdef YELLOWFIN_DEBUG +int yellowfin_debug = YELLOWFIN_DEBUG; +#else +int yellowfin_debug = 1; +#endif + +/* + Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Packet Engines "Yellowfin" Gigabit +Ethernet adapter. The only PCA currently supported is the G-NIC 64-bit +PCI card. + +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. + +III. Driver operation + +IIIa. Ring buffers + +The Yellowfin uses the Descriptor Based DMA Architecture specified by Apple. +This is a descriptor list scheme similar to that used by the EEPro100 and +Tulip. This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the Yellowfin as receive data +buffers. When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack and replaced by a newly allocated skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information). For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. + +IIIC. Synchronization + +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'yp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'yp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. + +IVb. References + +Yellowfin Engineering Design Specification, 4/23/97 Preliminary/Confidential +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html + +IVc. Errata + +See Packet Engines confidential appendix. + +*/ + +/* A few values that may be tweaked. */ +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +#ifndef PCI_VENDOR_ID_PKT_ENG /* To be defined in linux/pci.h */ +#define PCI_VENDOR_ID_PKT_ENG 0x1000 /* Hmm, likely number.. */ +#define PCI_DEVICE_ID_YELLOWFIN 0x0702 +#endif + +/* The rest of these values should never change. */ + +static void yellowfin_timer(unsigned long data); + +/* Offsets to the Yellowfin registers. Various sizes and alignments. */ +enum yellowfin_offsets { + TxCtrl=0x00, TxStatus=0x04, TxPtr=0x0C, + TxIntrSel=0x10, TxBranchSel=0x14, TxWaitSel=0x18, + RxCtrl=0x40, RxStatus=0x44, RxPtr=0x4C, + RxIntrSel=0x50, RxBranchSel=0x54, RxWaitSel=0x58, + EventStatus=0x80, IntrEnb=0x82, IntrClear=0x84, IntrStatus=0x86, + ChipRev=0x8C, DMACtrl=0x90, Cnfg=0xA0, RxDepth=0xB8, FlowCtrl=0xBC, + AddrMode=0xD0, StnAddr=0xD2, HashTbl=0xD8, FIFOcfg=0xF8, +}; + +/* The Yellowfin Rx and Tx buffer descriptors. */ +struct yellowfin_desc { + u16 request_cnt; + u16 cmd; + u32 addr; + u32 branch_addr; + u16 result_cnt; + u16 status; +}; + +struct tx_status_words { + u16 tx_cnt; + u16 tx_errs; + u16 total_tx_cnt; + u16 paused; +}; + +/* Bits in yellowfin_desc.cmd */ +enum desc_cmd_bits { + CMD_TX_PKT=0x1000, CMD_RX_BUF=0x2000, CMD_TXSTATUS=0x3000, + CMD_NOP=0x6000, CMD_STOP=0x7000, + BRANCH_ALWAYS=0x0C, INTR_ALWAYS=0x30, WAIT_ALWAYS=0x03, + BRANCH_IFTRUE=0x04, +}; + +/* Bits in yellowfin_desc.status */ +enum desc_status_bits { RX_EOP=0x0040, }; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { + IntrRxDone=0x01, IntrRxInvalid=0x02, IntrRxPCIFault=0x04,IntrRxPCIErr=0x08, + IntrTxDone=0x10, IntrTxInvalid=0x20, IntrTxPCIFault=0x40,IntrTxPCIErr=0x80, + IntrEarlyRx=0x100, IntrWakeup=0x200, }; + +struct yellowfin_private { + /* Descriptor rings first for alignment. Tx requires a second descriptor + for status. */ + struct yellowfin_desc rx_ring[RX_RING_SIZE]; + struct yellowfin_desc tx_ring[TX_RING_SIZE*2]; + const char *product_name; + struct device *next_module; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff* tx_skbuff[TX_RING_SIZE]; + struct tx_status_words tx_status[TX_RING_SIZE]; + /* The addresses of receive-in-place skbuffs. */ + struct sk_buff* rx_skbuff[RX_RING_SIZE]; + int chip_id; + struct enet_statistics stats; + struct timer_list timer; /* Media selection timer. */ + int in_interrupt; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int medialock:1; /* Do not sense media. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + u32 pad[4]; /* Used for 32-byte alignment */ +}; + +#ifdef MODULE +/* Used to pass the media type, 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}; + +#if LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit Ethernet driver"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(min_pci_latency, "i"); +MODULE_PARM(mtu, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif + +#endif + +static struct device *yellowfin_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int options); +static int yellowfin_open(struct device *dev); +static void yellowfin_timer(unsigned long data); +static void yellowfin_tx_timeout(struct device *dev); +static void yellowfin_init_ring(struct device *dev); +static int yellowfin_start_xmit(struct sk_buff *skb, struct device *dev); +static int yellowfin_rx(struct device *dev); +static void yellowfin_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); +static int yellowfin_close(struct device *dev); +static struct enet_statistics *yellowfin_get_stats(struct device *dev); +#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 + + + +#ifdef MODULE +/* A list of all installed Yellowfin devices, for removing the driver module. */ +static struct device *root_yellowfin_dev = NULL; +#endif + +int yellowfin_probe(struct device *dev) +{ + int cards_found = 0; + static int pci_index = 0; /* Static, for multiple probe calls. */ + + /* 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 + Yellowfin 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, vendor, device; + u32 pci_ioaddr, chip_idx = 0; + +#ifdef REVERSE_PROBE_ORDER + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + 0xfe - pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + continue; +#else + if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, + pci_index, + &pci_bus, &pci_device_fn) + != PCIBIOS_SUCCESSFUL) + break; +#endif + 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_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 (vendor != PCI_VENDOR_ID_PKT_ENG) + continue; + + if (device != PCI_DEVICE_ID_YELLOWFIN) + continue; + + if (yellowfin_debug > 2) + printk("Found Packet Engines Yellowfin G-NIC at I/O %#x, IRQ %d.\n", + pci_ioaddr, pci_irq_line); + + if (check_region(pci_ioaddr, YELLOWFIN_TOTAL_SIZE)) + continue; + +#ifdef MODULE + dev = yellowfin_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, + cards_found < MAX_UNITS ? options[cards_found] : 0); +#else + dev = yellowfin_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, + dev ? dev->mem_start : 0); +#endif + + if (dev) { + /* Get and check the bus-master and latency values. */ + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + if ( ! (pci_command & PCI_COMMAND_MASTER)) { + printk(" PCI Master Bit has not been set! Setting...\n"); + pci_command |= PCI_COMMAND_MASTER; + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, pci_command); + } + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < min_pci_latency) { + printk(" PCI latency timer (CFLT) is unreasonably low at %d." + " Setting to %d clocks.\n", + pci_latency, min_pci_latency); + pcibios_write_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, min_pci_latency); + } else if (yellowfin_debug > 1) + printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency); + dev = 0; + cards_found++; + } + } + } + +#if defined (MODULE) + return cards_found; +#else + return 0; +#endif +} + +static struct device *yellowfin_probe1(struct device *dev, int ioaddr, int irq, + int chip_id, int options) +{ + static int did_version = 0; /* Already printed version info. */ + struct yellowfin_private *yp; + int i; + + if (yellowfin_debug > 0 && did_version++ == 0) + printk(version); + + dev = init_etherdev(dev, sizeof(struct yellowfin_private)); + + printk("%s: P-E Yellowfin type %8x at %#3x, ", + dev->name, inl(ioaddr + ChipRev), ioaddr); + + for (i = 0; i < 5; i++) + printk("%2.2x:", inb(ioaddr + StnAddr + i)); + printk("%2.2x, IRQ %d.\n", inb(ioaddr + StnAddr + i), irq); + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb(ioaddr + StnAddr + i); + + /* Reset the chip. */ + outl(0x80000000, ioaddr + DMACtrl); + + + /* We do a request_region() only to register /proc/ioports info. */ + request_region(ioaddr, YELLOWFIN_TOTAL_SIZE, card_name); + + dev->base_addr = ioaddr; + dev->irq = irq; + + /* Make certain the descriptor lists are aligned. */ + yp = (void *)(((long)kmalloc(sizeof(*yp), GFP_KERNEL) + 31) & ~31); + memset(yp, 0, sizeof(*yp)); + dev->priv = yp; + +#ifdef MODULE + yp->next_module = root_yellowfin_dev; + root_yellowfin_dev = dev; +#endif + + yp->chip_id = chip_id; + + yp->full_duplex = 1; +#ifdef YELLOWFIN_DEFAULT_MEDIA + yp->default_port = YELLOWFIN_DEFAULT_MEDIA; +#endif +#ifdef YELLOWFIN_NO_MEDIA_SWITCH + yp->medialock = 1; +#endif + + /* The lower four bits are the media type. */ + if (options > 0) { + yp->full_duplex = (options & 16) ? 1 : 0; + yp->default_port = options & 15; + if (yp->default_port) + yp->medialock = 1; + } + + /* The Yellowfin-specific entries in the device structure. */ + dev->open = &yellowfin_open; + dev->hard_start_xmit = &yellowfin_start_xmit; + dev->stop = &yellowfin_close; + dev->get_stats = &yellowfin_get_stats; + dev->set_multicast_list = &set_rx_mode; + if (mtu) + dev->mtu = mtu; + + /* todo: Reset the xcvr interface and turn on heartbeat. */ + + return dev; +} + + +static int +yellowfin_open(struct device *dev) +{ + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int ioaddr = dev->base_addr; + + /* Reset the chip. */ + outl(0x80000000, ioaddr + DMACtrl); + +#ifdef SA_SHIRQ + if (request_irq(dev->irq, &yellowfin_interrupt, SA_SHIRQ, + card_name, dev)) { + return -EAGAIN; + } +#else + if (irq2dev_map[dev->irq] != NULL + || (irq2dev_map[dev->irq] = dev) == NULL + || dev->irq == 0 + || request_irq(dev->irq, &yellowfin_interrupt, 0, card_name)) { + return -EAGAIN; + } +#endif + + if (yellowfin_debug > 1) + printk("%s: yellowfin_open() irq %d.\n", dev->name, dev->irq); + + MOD_INC_USE_COUNT; + + yellowfin_init_ring(dev); + + outl(virt_to_bus(yp->rx_ring), ioaddr + RxPtr); + outl(virt_to_bus(yp->tx_ring), ioaddr + TxPtr); + + /* Set up various condition 'select' registers. + There are no options here. */ + outl(0x00800080, ioaddr + TxIntrSel); /* Interrupt on Tx abort */ + outl(0x00800080, ioaddr + TxBranchSel); /* Branch on Tx abort */ + outl(0x00400040, ioaddr + TxWaitSel); /* Wait on Tx status */ + outl(0x00400040, ioaddr + RxIntrSel); /* Interrupt on Rx done */ + outl(0x00400040, ioaddr + RxBranchSel); /* Branch on Rx error */ + outl(0x00400040, ioaddr + RxWaitSel); /* Wait on Rx done */ + + /* Initialize other registers: with so many this eventually this will + converted to an offset/value list. */ + outl(dma_ctrl, ioaddr + DMACtrl); + outw(fifo_cfg, ioaddr + FIFOcfg); + /* Enable automatic generation of flow control frames, period 0xffff. */ + outl(0x0030FFFF, ioaddr + FlowCtrl); + + if (dev->if_port == 0) + dev->if_port = yp->default_port; + + dev->tbusy = 0; + dev->interrupt = 0; + yp->in_interrupt = 0; + + /* We are always in full-duplex mode with the current chip! */ + yp->full_duplex = 1; + + /* Setting the Rx mode will start the Rx process. */ + outw(0x01CD | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); +#ifdef NEW_MULTICAST + set_rx_mode(dev); +#else + set_rx_mode(dev, 0, 0); +#endif + + dev->start = 1; + + /* Enable interrupts by setting the interrupt mask. */ + outw(0x81ff, ioaddr + IntrEnb); /* See enum intr_status_bits */ + outw(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ + outl(0x80008000, ioaddr + RxCtrl); /* Start Rx and Tx channels. */ + outl(0x80008000, ioaddr + TxCtrl); + + if (yellowfin_debug > 2) { + printk("%s: Done yellowfin_open().\n", + dev->name); + } + /* Set the timer to check for link beat. */ + init_timer(&yp->timer); + yp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + yp->timer.data = (unsigned long)dev; + yp->timer.function = &yellowfin_timer; /* timer handler */ + add_timer(&yp->timer); + + return 0; +} + +static void yellowfin_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int ioaddr = dev->base_addr; + int next_tick = 0; + + if (yellowfin_debug > 3) { + printk("%s: Yellowfin timer tick, status %8.8x.\n", + dev->name, inl(ioaddr + IntrStatus)); + } + if (next_tick) { + yp->timer.expires = RUN_AT(next_tick); + add_timer(&yp->timer); + } +} + +static void yellowfin_tx_timeout(struct device *dev) +{ + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int ioaddr = dev->base_addr; + + printk("%s: Yellowfin transmit timed out, status %8.8x, resetting...\n", + dev->name, inl(ioaddr)); + +#ifndef __alpha__ + { + int i; + printk(" Rx ring %8.8x: ", (int)yp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)yp->rx_ring[i].status); + printk("\n Tx ring %8.8x: ", (int)yp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %4.4x /%4.4x", yp->tx_status[i].tx_errs, yp->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 . */ + + /* Trigger an immediate transmit demand. */ + + dev->trans_start = jiffies; + yp->stats.tx_errors++; + return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +yellowfin_init_ring(struct device *dev) +{ + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int i; + + yp->tx_full = 0; + yp->cur_rx = yp->cur_tx = 0; + yp->dirty_rx = yp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + struct sk_buff *skb; + int pkt_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); + + yp->rx_ring[i].request_cnt = pkt_buf_sz; + yp->rx_ring[i].cmd = CMD_RX_BUF | INTR_ALWAYS; + + skb = DEV_ALLOC_SKB(pkt_buf_sz); + skb_reserve(skb, 2); /* 16 byte align the IP header. */ + yp->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 + yp->rx_ring[i].addr = virt_to_bus(skb->tail); +#else + yp->rx_ring[i].addr = virt_to_bus(skb->data); +#endif + yp->rx_ring[i].branch_addr = virt_to_bus(&yp->rx_ring[i+1]); + } + /* Mark the last entry as wrapping the ring. */ + yp->rx_ring[i-1].cmd = CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS; + yp->rx_ring[i-1].branch_addr = virt_to_bus(&yp->rx_ring[0]); + +/*#define NO_TXSTATS*/ +#ifdef NO_TXSTATS + /* In this mode the Tx ring needs only a single descriptor. */ + for (i = 0; i < TX_RING_SIZE; i++) { + yp->tx_skbuff[i] = 0; + yp->tx_ring[i].cmd = CMD_STOP; + yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); + } + yp->tx_ring[--i].cmd = CMD_STOP | BRANCH_ALWAYS; /* Wrap ring */ + yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]); +#else + /* Tx ring needs a pair of descriptors, the second for the status. */ + for (i = 0; i < TX_RING_SIZE*2; i++) { + yp->tx_skbuff[i/2] = 0; + yp->tx_ring[i].cmd = CMD_STOP; /* Branch on Tx error. */ + yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); + i++; + yp->tx_ring[i].cmd = CMD_TXSTATUS; /* Interrupt, no wait. */ + yp->tx_ring[i].request_cnt = sizeof(yp->tx_status[i]); + yp->tx_ring[i].addr = virt_to_bus(&yp->tx_status[i/2]); + yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); + } + /* Wrap ring */ + yp->tx_ring[--i].cmd = CMD_TXSTATUS | BRANCH_ALWAYS | INTR_ALWAYS; + yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]); +#endif +} + +static int +yellowfin_start_xmit(struct sk_buff *skb, struct device *dev) +{ + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + unsigned entry; + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + yellowfin_tx_timeout(dev); + return 1; + } + + /* Caution: the write order is important here, set the base address + with the "ownership" bits last. */ + + /* Calculate the next Tx descriptor entry. */ + entry = yp->cur_tx % TX_RING_SIZE; + + yp->tx_skbuff[entry] = skb; + +#ifdef NO_TXSTATS + yp->tx_ring[entry].request_cnt = skb->len; + yp->tx_ring[entry].addr = virt_to_bus(skb->data); + yp->tx_ring[entry].status = 0; + if (entry >= TX_RING_SIZE-1) { + yp->tx_ring[0].cmd = CMD_STOP; /* New stop command. */ + yp->tx_ring[TX_RING_SIZE-1].cmd = CMD_TX_PKT | BRANCH_ALWAYS; + } else { + yp->tx_ring[entry+1].cmd = CMD_STOP; /* New stop command. */ + yp->tx_ring[entry].cmd = CMD_TX_PKT | BRANCH_IFTRUE; + } + yp->cur_tx++; +#else + yp->tx_ring[entry<<1].request_cnt = skb->len; + yp->tx_ring[entry<<1].addr = virt_to_bus(skb->data); + /* The input_last (status-write) command is constant, but we must rewrite + the subsequent 'stop' command. */ + + yp->cur_tx++; + { + unsigned next_entry = yp->cur_tx % TX_RING_SIZE; + yp->tx_ring[next_entry<<1].cmd = CMD_STOP; + } + /* Final step -- overwrite the old 'stop' command. */ + + yp->tx_ring[entry<<1].cmd = + (entry % 6) == 0 ? CMD_TX_PKT | INTR_ALWAYS | BRANCH_IFTRUE : + CMD_TX_PKT | BRANCH_IFTRUE; +#endif + + /* Todo: explicitly flush cache lines here. */ + + /* Wake the potentially-idle transmit channel. */ + outl(0x10001000, dev->base_addr + TxCtrl); + + if (yp->cur_tx - yp->dirty_tx < TX_RING_SIZE - 1) + clear_bit(0, (void*)&dev->tbusy); /* Typical path */ + else + yp->tx_full = 1; + dev->trans_start = jiffies; + + if (yellowfin_debug > 4) { + printk("%s: Yellowfin transmit frame #%d queued in slot %d.\n", + dev->name, yp->cur_tx, entry); + } + return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static void yellowfin_interrupt IRQ(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 yellowfin_private *lp; + int ioaddr, boguscnt = max_interrupt_work; + + if (dev == NULL) { + printk ("yellowfin_interrupt(): irq %d for unknown device.\n", irq); + return; + } + + ioaddr = dev->base_addr; + lp = (struct yellowfin_private *)dev->priv; + if (test_and_set_bit(0, (void*)&lp->in_interrupt)) { + dev->interrupt = 1; + printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); + return; + } + + do { + u16 intr_status = inw(ioaddr + IntrClear); + unsigned dirty_tx = lp->dirty_tx; + + if (yellowfin_debug > 4) + printk("%s: Yellowfin interrupt, status %4.4x.\n", + dev->name, intr_status); + + if (intr_status == 0) + break; + + if (intr_status & (IntrRxDone | IntrEarlyRx)) + yellowfin_rx(dev); + +#ifdef NO_TXSTATS + for (; lp->cur_tx - dirty_tx > 0; dirty_tx++) { + int entry = dirty_tx % TX_RING_SIZE; + if (lp->tx_ring[entry].status == 0) + break; + /* Free the original skb. */ + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + lp->stats.tx_packets++; + } + if (lp->tx_full && dev->tbusy + && lp->cur_tx - dirty_tx < TX_RING_SIZE - 4) { + /* The ring is no longer full, clear tbusy. */ + lp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + lp->dirty_tx = dirty_tx; +#else + if (intr_status & IntrTxDone + || lp->tx_status[dirty_tx % TX_RING_SIZE].tx_errs) { + + for (dirty_tx = lp->dirty_tx; lp->cur_tx - dirty_tx > 0; + dirty_tx++) { + /* Todo: optimize this. */ + int entry = dirty_tx % TX_RING_SIZE; + u16 tx_errs = lp->tx_status[entry].tx_errs; + + if (tx_errs == 0) + break; /* It still hasn't been Txed */ + if (tx_errs & 0xF8100000) { + /* There was an major error, log it. */ +#ifndef final_version + if (yellowfin_debug > 1) + printk("%s: Transmit error, Tx status %4.4x.\n", + dev->name, tx_errs); +#endif + lp->stats.tx_errors++; + if (tx_errs & 0xF800) lp->stats.tx_aborted_errors++; + if (tx_errs & 0x0800) lp->stats.tx_carrier_errors++; + if (tx_errs & 0x2000) lp->stats.tx_window_errors++; + if (tx_errs & 0x8000) lp->stats.tx_fifo_errors++; +#ifdef ETHER_STATS + if (tx_errs & 0x1000) lp->stats.collisions16++; +#endif + } else { +#ifdef ETHER_STATS + if (status & 0x0400) lp->stats.tx_deferred++; +#endif + lp->stats.collisions += tx_errs & 15; + lp->stats.tx_packets++; + } + + /* Free the original skb. */ + dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); + lp->tx_skbuff[entry] = 0; + /* Mark status as empty. */ + lp->tx_status[entry].tx_errs = 0; + } + +#ifndef final_version + if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + dirty_tx += TX_RING_SIZE; + } +#endif + + if (lp->tx_full && dev->tbusy + && lp->cur_tx - dirty_tx < TX_RING_SIZE - 2) { + /* The ring is no longer full, clear tbusy. */ + lp->tx_full = 0; + clear_bit(0, (void*)&dev->tbusy); + mark_bh(NET_BH); + } + + lp->dirty_tx = dirty_tx; + } +#endif + + /* Log errors and other events. */ + if (intr_status & 0x2ee) { /* Abnormal error summary. */ + printk("%s: Something Wicked happened! %4.4x.\n", + dev->name, intr_status); + /* Hmmmmm, it's not clear what to do here. */ + if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) + lp->stats.tx_errors++; + if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) + lp->stats.rx_errors++; + } + if (--boguscnt < 0) { + printk("%s: Too much work at interrupt, status=0x%4.4x.\n", + dev->name, intr_status); + break; + } + } while (1); + + if (yellowfin_debug > 3) + printk("%s: exiting interrupt, status=%#4.4x.\n", + dev->name, inw(ioaddr + IntrStatus)); + + /* Code that should never be run! Perhaps remove after testing.. */ + { + static int stopit = 10; + if (dev->start == 0 && --stopit < 0) { + printk("%s: Emergency stop, looping startup interrupt.\n", + dev->name); +#ifdef SA_SHIRQ + free_irq(irq, dev); +#else + free_irq(irq); +#endif + } + } + + dev->interrupt = 0; + clear_bit(0, (void*)&lp->in_interrupt); + return; +} + +/* This routine is logically part of the interrupt handler, but seperated + for clarity and better register allocation. */ +static int +yellowfin_rx(struct device *dev) +{ + struct yellowfin_private *lp = (struct yellowfin_private *)dev->priv; + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int entry = lp->cur_rx % RX_RING_SIZE; + int boguscnt = 20; + + if (yellowfin_debug > 4) { + printk(" In yellowfin_rx(), entry %d status %4.4x.\n", entry, + yp->rx_ring[entry].status); + printk(" #%d desc. %4.4x %4.4x %8.8x %4.4x %4.4x.\n", + entry, yp->rx_ring[entry].cmd, + yp->rx_ring[entry].request_cnt, yp->rx_ring[entry].addr, + yp->rx_ring[entry].result_cnt, yp->rx_ring[entry].status); + } + + + /* If EOP is set on the next entry, it's a new packet. Send it up. */ + while (yp->rx_ring[entry].status) { + /* Todo: optimize this mess. */ + u16 desc_status = yp->rx_ring[entry].status; + struct yellowfin_desc *desc = &lp->rx_ring[entry]; + int frm_size = desc->request_cnt - desc->result_cnt; + u8 *buf_addr = bus_to_virt(lp->rx_ring[entry].addr); + s16 frame_status = *(s16*)&(buf_addr[frm_size - 2]); + + if (yellowfin_debug > 4) + printk(" yellowfin_rx() status was %4.4x.\n", frame_status); + if (--boguscnt < 0) + break; + if ( ! (desc_status & RX_EOP)) { + printk("%s: Oversized Ethernet frame spanned multiple buffers," + " status %4.4x!\n", dev->name, desc_status); + lp->stats.rx_length_errors++; + } else if (frame_status & 0x0038) { + /* There was a error. */ + if (yellowfin_debug > 3) + printk(" yellowfin_rx() Rx error was %4.4x.\n", frame_status); + lp->stats.rx_errors++; + if (frame_status & 0x0060) lp->stats.rx_length_errors++; + if (frame_status & 0x0008) lp->stats.rx_frame_errors++; + if (frame_status & 0x0010) lp->stats.rx_crc_errors++; + if (frame_status < 0) lp->stats.rx_dropped++; +#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ + } else if (memcmp(bus_to_virt(lp->rx_ring[entry].addr), + dev->dev_addr, 6) != 0 + && memcmp(bus_to_virt(lp->rx_ring[entry].addr), + "\377\377\377\377\377\377", 6) != 0) { + printk("%s: Bad frame to %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", + dev->name, + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[0], + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[1], + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[2], + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[3], + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[4], + ((char *)bus_to_virt(lp->rx_ring[entry].addr))[5]); + bogus_rx++; +#endif + } else { + u8 bogus_cnt = buf_addr[frm_size - 8]; + int pkt_len = frm_size - 8 - bogus_cnt; + 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; + + /* Get a fresh skbuff to replace the filled one. */ + newskb = DEV_ALLOC_SKB(dev->mtu <= 1500 ? PKT_BUF_SZ + : dev->mtu + 32); + if (newskb == NULL) { + skb = 0; /* No memory, drop the packet. */ + goto memory_squeeze; + } + /* Pass up the skb already on the Rx ring. */ + skb = lp->rx_skbuff[entry]; + temp = skb_put(skb, pkt_len); + if (bus_to_virt(lp->rx_ring[entry].addr) != temp) + printk("%s: Warning -- the skbuff addresses do not match" + " in yellowfin_rx: %p vs. %p / %p.\n", dev->name, + bus_to_virt(lp->rx_ring[entry].addr), + skb->head, temp); + rx_in_place = 1; + lp->rx_skbuff[entry] = newskb; + newskb->dev = dev; + skb_reserve(newskb, 2); /* 16 byte align IP header */ + lp->rx_ring[entry].addr = virt_to_bus(newskb->tail); + } else + skb = DEV_ALLOC_SKB(pkt_len + 2); + memory_squeeze: + if (skb == NULL) { + printk("%s: Memory squeeze, deferring packet.\n", dev->name); + /* todo: Check that at least two ring entries are free. + If not, free one and mark stats->rx_dropped++. */ + break; + } + skb->dev = dev; + if (! rx_in_place) { + skb_reserve(skb, 2); /* 16 byte align the data fields */ + memcpy(skb_put(skb, pkt_len), + bus_to_virt(lp->rx_ring[entry].addr), pkt_len); + } +#if LINUX_VERSION_CODE > 0x10300 + skb->protocol = eth_type_trans(skb, dev); +#else + skb->len = pkt_len; +#endif + netif_rx(skb); + lp->stats.rx_packets++; + } + + /* Mark this entry as being the end-of-list, and the prior entry + as now valid. */ + lp->rx_ring[entry].cmd = CMD_STOP; + yp->rx_ring[entry].status = 0; + { + int prev_entry = entry - 1; + if (prev_entry < 0) + lp->rx_ring[RX_RING_SIZE - 1].cmd = + CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS; + else + lp->rx_ring[prev_entry].cmd = CMD_RX_BUF | INTR_ALWAYS; + } + entry = (++lp->cur_rx) % RX_RING_SIZE; + } + /* todo: restart Rx engine if stopped. For now we just make the Rx ring + large enough to avoid this. */ + + return 0; +} + +static int +yellowfin_close(struct device *dev) +{ + int ioaddr = dev->base_addr; + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + int i; + + dev->start = 0; + dev->tbusy = 1; + + if (yellowfin_debug > 1) { + printk("%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n", + dev->name, inw(ioaddr + TxStatus), + inw(ioaddr + RxStatus), inl(ioaddr + IntrStatus)); + printk("%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", + dev->name, yp->cur_tx, yp->dirty_tx, yp->cur_rx, yp->dirty_rx); + } + + /* Disable interrupts by clearing the interrupt mask. */ + outw(0x0000, ioaddr + IntrEnb); + + /* Stop the chip's Tx and Rx processes. */ + outl(0x80000000, ioaddr + RxCtrl); + outl(0x80000000, ioaddr + TxCtrl); + + del_timer(&yp->timer); + +#ifdef __i386__ + if (yellowfin_debug > 2) { + printk("\n Tx ring at %8.8x:\n", (int)virt_to_bus(yp->tx_ring)); + for (i = 0; i < TX_RING_SIZE*2; i++) + printk(" %c #%d desc. %4.4x %4.4x %8.8x %8.8x %4.4x %4.4x.\n", + inl(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', + i, yp->tx_ring[i].cmd, + yp->tx_ring[i].request_cnt, yp->tx_ring[i].addr, + yp->tx_ring[i].branch_addr, + yp->tx_ring[i].result_cnt, yp->tx_ring[i].status); + printk(" Tx status %p:\n", yp->tx_status); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" #%d status %4.4x %4.4x %4.4x %4.4x.\n", + i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, + yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); + + printk("\n Rx ring %8.8x:\n", (int)virt_to_bus(yp->rx_ring)); + for (i = 0; i < RX_RING_SIZE; i++) { + printk(" %c #%d desc. %4.4x %4.4x %8.8x %4.4x %4.4x\n", + inl(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ', + i, yp->rx_ring[i].cmd, + yp->rx_ring[i].request_cnt, yp->rx_ring[i].addr, + yp->rx_ring[i].result_cnt, yp->rx_ring[i].status); + if (yellowfin_debug > 5) { + if (*(u8*)yp->rx_ring[i].addr != 0x69) { + int j; + for (j = 0; j < 0x50; j++) + printk(" %4.4x", ((u16*)yp->rx_ring[i].addr)[j]); + printk("\n"); + } + } + } + } +#endif /* __i386__ debugging only */ + +#ifdef SA_SHIRQ + free_irq(dev->irq, dev); +#else + free_irq(dev->irq); + irq2dev_map[dev->irq] = 0; +#endif + + /* Free all the skbuffs in the Rx queue. */ + for (i = 0; i < RX_RING_SIZE; i++) { + yp->rx_ring[i].cmd = CMD_STOP; + yp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ + if (yp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 + yp->rx_skbuff[i]->free = 1; +#endif + dev_kfree_skb(yp->rx_skbuff[i], FREE_WRITE); + } + yp->rx_skbuff[i] = 0; + } + for (i = 0; i < TX_RING_SIZE; i++) { + if (yp->tx_skbuff[i]) + dev_kfree_skb(yp->tx_skbuff[i], FREE_WRITE); + yp->tx_skbuff[i] = 0; + } + +#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ + if (yellowfin_debug > 0) { + printk("%s: Received %d frames that we should not have.\n", + dev->name, bogus_rx); + } +#endif + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct enet_statistics * +yellowfin_get_stats(struct device *dev) +{ + struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + return &yp->stats; +} + +/* Set or clear the multicast filter for this adaptor. */ + +/* The little-endian AUTODIN32 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) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + 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; +} + + +#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 +{ + int ioaddr = dev->base_addr; + u16 cfg_value = inw(ioaddr + Cnfg); + + /* Stop the Rx process to change any value. */ + outw(cfg_value & ~0x1000, ioaddr + Cnfg); + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + /* Unconditionally log net taps. */ + printk("%s: Promiscuous mode enabled.\n", dev->name); + outw(0x000F, ioaddr + AddrMode); + } else if ((dev->mc_count > 64) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter well, or accept all multicasts. */ + outw(0x000B, ioaddr + AddrMode); + } else if (dev->mc_count > 0) { /* Must use the multicast hash table. */ + struct dev_mc_list *mclist; + u16 hash_table[4]; + int i; + memset(hash_table, 0, sizeof(hash_table)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + /* Due to a bug in the early chip versions, multiple filter + slots must be set for each address. */ + set_bit((ether_crc_le(3, mclist->dmi_addr) >> 3) & 0x3f, + hash_table); + set_bit((ether_crc_le(4, mclist->dmi_addr) >> 3) & 0x3f, + hash_table); + set_bit((ether_crc_le(5, mclist->dmi_addr) >> 3) & 0x3f, + hash_table); + set_bit((ether_crc_le(6, mclist->dmi_addr) >> 3) & 0x3f, + hash_table); + } + /* Copy the hash table to the chip. */ + for (i = 0; i < 4; i++) + outw(hash_table[i], ioaddr + HashTbl + i*2); + outw(0x0003, ioaddr + AddrMode); + } else { /* Normal, unicast/broadcast-only mode. */ + outw(0x0001, ioaddr + AddrMode); + } + /* Restart the Rx process. */ + outw(cfg_value | 0x1000, ioaddr + Cnfg); +} + +#ifdef MODULE + +/* An additional parameter that may be passed in... */ +static int debug = -1; + +int +init_module(void) +{ + int cards_found; + + if (debug >= 0) + yellowfin_debug = debug; + + root_yellowfin_dev = NULL; + cards_found = yellowfin_probe(0); + + return cards_found ? 0 : -ENODEV; +} + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_yellowfin_dev) { + next_dev = ((struct yellowfin_private *)root_yellowfin_dev->priv)->next_module; + unregister_netdev(root_yellowfin_dev); + release_region(root_yellowfin_dev->base_addr, YELLOWFIN_TOTAL_SIZE); + kfree(root_yellowfin_dev); + root_yellowfin_dev = next_dev; + } +} + +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c yellowfin.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -u --recursive --new-file v2.0.33/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.0.33/linux/drivers/pci/pci.c Tue Dec 2 13:52:31 1997 +++ linux/drivers/pci/pci.c Wed Jun 3 15:17:48 1998 @@ -5,7 +5,10 @@ * * Copyright 1993, 1994, 1995 Drew Eckhardt, Frederic Potter, * David Mosberger-Tang + * + * Apr 12, 1998 : Fixed handling of alien header types. [mj] */ + #include #include #include @@ -42,10 +45,15 @@ */ struct pci_dev_info dev_info[] = { DEVICE( COMPAQ, COMPAQ_1280, "QVision 1280/p"), - DEVICE( COMPAQ, COMPAQ_NETELL100,"Netelligent 10/100"), - DEVICE( COMPAQ, COMPAQ_NETELL10,"Netelligent 10"), - DEVICE( COMPAQ, COMPAQ_NETFLEX3,"NetFlex 3"), + DEVICE( COMPAQ, COMPAQ_SMART2P, "Smart-2/P RAID Controller"), + DEVICE( COMPAQ, COMPAQ_NETEL100,"Netelligent 10/100"), + DEVICE( COMPAQ, COMPAQ_NETEL10, "Netelligent 10"), + DEVICE( COMPAQ, COMPAQ_NETFLEX3I,"NetFlex 3"), + DEVICE( COMPAQ, COMPAQ_NETEL100D,"Netelligent 10/100 Dual"), + DEVICE( COMPAQ, COMPAQ_NETEL100PI,"Netelligent 10/100 ProLiant"), + DEVICE( COMPAQ, COMPAQ_NETEL100I,"Netelligent 10/100 Integrated"), DEVICE( COMPAQ, COMPAQ_THUNDER, "ThunderLAN"), + DEVICE( COMPAQ, COMPAQ_NETFLEX3B,"NetFlex 3 BNC"), DEVICE( NCR, NCR_53C810, "53c810"), DEVICE( NCR, NCR_53C820, "53c820"), DEVICE( NCR, NCR_53C825, "53c825"), @@ -55,18 +63,34 @@ DEVICE( NCR, NCR_53C895, "53c895"), DEVICE( NCR, NCR_53C885, "53c885"), DEVICE( NCR, NCR_53C875, "53c875"), + DEVICE( NCR, NCR_53C875J, "53c875J"), DEVICE( ATI, ATI_68800, "68800AX"), DEVICE( ATI, ATI_215CT222, "215CT222"), DEVICE( ATI, ATI_210888CX, "210888CX"), + DEVICE( ATI, ATI_215GB, "Mach64 GB"), + DEVICE( ATI, ATI_215GD, "Mach64 GD (Rage Pro)"), + DEVICE( ATI, ATI_215GI, "Mach64 GI (Rage Pro)"), + DEVICE( ATI, ATI_215GP, "Mach64 GP (Rage Pro)"), + DEVICE( ATI, ATI_215GQ, "Mach64 GQ (Rage Pro)"), DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"), + DEVICE( ATI, ATI_215GTB, "Mach64 GT (Rage II)"), DEVICE( ATI, ATI_210888GX, "210888GX"), + DEVICE( ATI, ATI_215LG, "Mach64 LG (3D Rage LT)"), + DEVICE( ATI, ATI_264LT, "Mach64 LT"), DEVICE( ATI, ATI_264VT, "Mach64 VT"), DEVICE( VLSI, VLSI_82C592, "82C592-FC1"), DEVICE( VLSI, VLSI_82C593, "82C593-FC1"), DEVICE( VLSI, VLSI_82C594, "82C594-AFC2"), DEVICE( VLSI, VLSI_82C597, "82C597-AFC2"), - DEVICE( VLSI, VLSI_VAS96011, "VAS96011 PowerPC"), + DEVICE( VLSI, VLSI_82C541, "82C541 Lynx"), + DEVICE( VLSI, VLSI_82C543, "82C543 Lynx ISA"), + DEVICE( VLSI, VLSI_82C532, "82C532"), + DEVICE( VLSI, VLSI_82C534, "82C534"), + DEVICE( VLSI, VLSI_82C535, "82C535"), + DEVICE( VLSI, VLSI_82C147, "82C147"), + DEVICE( VLSI, VLSI_VAS96011, "VAS96011 (Golden Gate II)"), DEVICE( ADL, ADL_2301, "2301"), + DEVICE( NS, NS_87415, "87415"), DEVICE( NS, NS_87410, "87410"), DEVICE( TSENG, TSENG_W32P_2, "ET4000W32P"), DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"), @@ -77,12 +101,14 @@ DEVICE( WEITEK, WEITEK_P9100, "P9100"), BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), DEVICE( DEC, DEC_TULIP, "DC21040"), - DEVICE( DEC, DEC_TGA, "DC21030"), + DEVICE( DEC, DEC_TGA, "DC21030 (TGA)"), DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), + DEVICE( DEC, DEC_TGA2, "TGA2"), DEVICE( DEC, DEC_FDDI, "DEFPA"), DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"), DEVICE( DEC, DEC_21142, "DC21142"), DEVICE( DEC, DEC_21052, "DC21052"), + DEVICE( DEC, DEC_21150, "DC21150"), DEVICE( DEC, DEC_21152, "DC21152"), DEVICE( CIRRUS, CIRRUS_7548, "GD 7548"), DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"), @@ -99,29 +125,41 @@ DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"), DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"), DEVICE( IBM, IBM_FIRE_CORAL, "Fire Coral"), + DEVICE( IBM, IBM_TR, "Token Ring"), DEVICE( IBM, IBM_82G2675, "82G2675"), + DEVICE( IBM, IBM_MCA, "MicroChannel"), DEVICE( IBM, IBM_82351, "82351"), + DEVICE( IBM, IBM_SERVERAID, "ServeRAID"), + DEVICE( IBM, IBM_TR_WAKE, "Wake On LAN Token Ring"), + DEVICE( IBM, IBM_3780IDSP, "MWave DSP"), DEVICE( WD, WD_7197, "WD 7197"), DEVICE( AMD, AMD_LANCE, "79C970"), DEVICE( AMD, AMD_SCSI, "53C974"), + DEVICE( TRIDENT, TRIDENT_9397, "Cyber9397"), DEVICE( TRIDENT, TRIDENT_9420, "TG 9420"), DEVICE( TRIDENT, TRIDENT_9440, "TG 9440"), - DEVICE( TRIDENT, TRIDENT_9660, "TG 9660"), + DEVICE( TRIDENT, TRIDENT_9660, "TG 9660 / Cyber9385"), + DEVICE( TRIDENT, TRIDENT_9750, "Image 975"), DEVICE( AI, AI_M1435, "M1435"), DEVICE( MATROX, MATROX_MGA_2, "Atlas PX2085"), DEVICE( MATROX, MATROX_MIL, "Millennium"), DEVICE( MATROX, MATROX_MYS, "Mystique"), DEVICE( MATROX, MATROX_MIL_2, "Millennium II"), + DEVICE( MATROX, MATROX_MIL_2_AGP,"Millennium II AGP"), DEVICE( MATROX, MATROX_MGA_IMP, "MGA Impression"), DEVICE( CT, CT_65545, "65545"), DEVICE( CT, CT_65548, "65548"), DEVICE( CT, CT_65550, "65550"), DEVICE( CT, CT_65554, "65554"), + DEVICE( CT, CT_65555, "65555"), DEVICE( MIRO, MIRO_36050, "ZR36050"), + DEVICE( NEC, NEC_PCX2, "PowerVR PCX2"), DEVICE( FD, FD_36C70, "TMC-18C30"), - DEVICE( SI, SI_6201, "6201"), + DEVICE( SI, SI_5591_AGP, "5591/5592 AGP"), DEVICE( SI, SI_6202, "6202"), DEVICE( SI, SI_503, "85C503"), + DEVICE( SI, SI_ACPI, "ACPI"), + DEVICE( SI, SI_5597_VGA, "5597/5598 VGA"), DEVICE( SI, SI_6205, "6205"), DEVICE( SI, SI_501, "85C501"), DEVICE( SI, SI_496, "85C496"), @@ -130,29 +168,42 @@ DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), DEVICE( SI, SI_5571, "5571"), - DEVICE( SI, SI_7001, "7001"), + DEVICE( SI, SI_5591, "5591/5592 Host"), + DEVICE( SI, SI_5597, "5597/5598 Host"), + DEVICE( SI, SI_7001, "7001 USB"), DEVICE( HP, HP_J2585A, "J2585A"), DEVICE( HP, HP_J2585B, "J2585B (Lassen)"), DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"), DEVICE( PCTECH, PCTECH_RZ1001, "RZ1001 (buggy?)"), + DEVICE( PCTECH, PCTECH_SAMURAI_0,"Samurai 0"), + DEVICE( PCTECH, PCTECH_SAMURAI_1,"Samurai 1"), + DEVICE( PCTECH, PCTECH_SAMURAI_IDE,"Samurai IDE"), DEVICE( DPT, DPT, "SmartCache/Raid"), DEVICE( OPTI, OPTI_92C178, "92C178"), - DEVICE( OPTI, OPTI_82C557, "82C557"), - DEVICE( OPTI, OPTI_82C558, "82C558"), + DEVICE( OPTI, OPTI_82C557, "82C557 Viper-M"), + DEVICE( OPTI, OPTI_82C558, "82C558 Viper-M ISA+IDE"), DEVICE( OPTI, OPTI_82C621, "82C621"), + DEVICE( OPTI, OPTI_82C700, "82C700"), + DEVICE( OPTI, OPTI_82C701, "82C701 FireStar Plus"), + DEVICE( OPTI, OPTI_82C814, "82C814 Firebridge 1"), DEVICE( OPTI, OPTI_82C822, "82C822"), + DEVICE( OPTI, OPTI_82C825, "82C825 Firebridge 2"), DEVICE( SGS, SGS_2000, "STG 2000X"), DEVICE( SGS, SGS_1764, "STG 1764X"), DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER_NC, "MultiMaster NC"), DEVICE( BUSLOGIC, BUSLOGIC_MULTIMASTER, "MultiMaster"), DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), + DEVICE( TI, TI_TVP4010, "TVP4010 Permedia"), + DEVICE( TI, TI_TVP4020, "TVP4020 Permedia 2"), DEVICE( TI, TI_PCI1130, "PCI1130"), DEVICE( TI, TI_PCI1131, "PCI1131"), + DEVICE( TI, TI_PCI1250, "PCI1250"), DEVICE( OAK, OAK_OTI107, "OTI107"), DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"), DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), + DEVICE( PROMISE, PROMISE_20246, "IDE UltraDMA/33"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), @@ -166,20 +217,36 @@ DEVICE( UMC, UMC_UM8886N, "UM8886N"), DEVICE( UMC, UMC_UM8891N, "UM8891N"), DEVICE( X, X_AGX016, "ITT AGX016"), + DEVICE( PICOP, PICOP_PT86C52X, "PT86C52x Vesuvius"), + DEVICE( PICOP, PICOP_PT80C524, "PT80C524 Nile"), DEVICE( APPLE, APPLE_BANDIT, "Bandit"), DEVICE( APPLE, APPLE_GC, "Grand Central"), DEVICE( APPLE, APPLE_HYDRA, "Hydra"), DEVICE( NEXGEN, NEXGEN_82C501, "82C501"), DEVICE( QLOGIC, QLOGIC_ISP1020, "ISP1020"), DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"), + DEVICE( CYRIX, CYRIX_5510, "5510"), + DEVICE( CYRIX, CYRIX_PCI_MASTER,"PCI Master"), + DEVICE( CYRIX, CYRIX_5520, "5520"), + DEVICE( CYRIX, CYRIX_5530_LEGACY,"5530 Kahlua Legacy"), + DEVICE( CYRIX, CYRIX_5530_SMI, "5530 Kahlua SMI"), + DEVICE( CYRIX, CYRIX_5530_IDE, "5530 Kahlua IDE"), + DEVICE( CYRIX, CYRIX_5530_AUDIO,"5530 Kahlua Audio"), + DEVICE( CYRIX, CYRIX_5530_VIDEO,"5530 Kahlua Video"), DEVICE( LEADTEK, LEADTEK_805, "S3 805"), DEVICE( CONTAQ, CONTAQ_82C599, "82C599"), + DEVICE( CONTAQ, CONTAQ_82C693, "82C693"), DEVICE( OLICOM, OLICOM_OC3136, "OC-3136/3137"), DEVICE( OLICOM, OLICOM_OC2315, "OC-2315"), DEVICE( OLICOM, OLICOM_OC2325, "OC-2325"), DEVICE( OLICOM, OLICOM_OC2183, "OC-2183/2185"), DEVICE( OLICOM, OLICOM_OC2326, "OC-2326"), DEVICE( OLICOM, OLICOM_OC6151, "OC-6151/6152"), + DEVICE( SUN, SUN_EBUS, "EBUS"), + DEVICE( SUN, SUN_HAPPYMEAL, "Happy Meal Ethernet"), + DEVICE( SUN, SUN_SIMBA, "Advanced PCI Bridge"), + DEVICE( SUN, SUN_PBM, "PCI Bus Module"), + DEVICE( SUN, SUN_SABRE, "Ultra IIi PCI"), DEVICE( CMD, CMD_640, "640 (buggy)"), DEVICE( CMD, CMD_643, "643"), DEVICE( CMD, CMD_646, "646"), @@ -187,12 +254,17 @@ DEVICE( VISION, VISION_QD8500, "QD-8500"), DEVICE( VISION, VISION_QD8580, "QD-8580"), DEVICE( BROOKTREE, BROOKTREE_848, "Bt848"), + DEVICE( BROOKTREE, BROOKTREE_849A, "Bt849"), + DEVICE( BROOKTREE, BROOKTREE_8474, "Bt8474"), DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"), DEVICE( ACC, ACC_2056, "2056"), DEVICE( WINBOND, WINBOND_83769, "W83769F"), DEVICE( WINBOND, WINBOND_82C105, "SL82C105"), DEVICE( WINBOND, WINBOND_83C553, "W83C553"), DEVICE( DATABOOK, DATABOOK_87144, "DB87144"), + DEVICE( PLX, PLX_9080, "PCI9080 I2O"), + DEVICE( MADGE, MADGE_MK2, "Smart 16/4 BM Mk2 Ringnode"), + DEVICE( 3COM, 3COM_3C339, "3C339 TokenRing"), DEVICE( 3COM, 3COM_3C590, "3C590 10bT"), DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"), DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"), @@ -200,6 +272,9 @@ DEVICE( 3COM, 3COM_3C900TPO, "3C900 10bTPO"), DEVICE( 3COM, 3COM_3C900COMBO,"3C900 10b Combo"), DEVICE( 3COM, 3COM_3C905TX, "3C905 100bTX"), + DEVICE( 3COM, 3COM_3C905T4, "3C905 100bT4"), + DEVICE( 3COM, 3COM_3C905B_TX, "3C905B 100bTX"), + DEVICE( SMC, SMC_EPIC100, "9432 TX"), DEVICE( AL, AL_M1445, "M1445"), DEVICE( AL, AL_M1449, "M1449"), DEVICE( AL, AL_M1451, "M1451"), @@ -207,32 +282,58 @@ DEVICE( AL, AL_M1489, "M1489"), DEVICE( AL, AL_M1511, "M1511"), DEVICE( AL, AL_M1513, "M1513"), + DEVICE( AL, AL_M1521, "M1521"), + DEVICE( AL, AL_M1523, "M1523"), + DEVICE( AL, AL_M1531, "M1531 Aladdin IV"), + DEVICE( AL, AL_M1533, "M1533 Aladdin IV"), + DEVICE( AL, AL_M3307, "M3307 MPEG-1 decoder"), DEVICE( AL, AL_M4803, "M4803"), + DEVICE( AL, AL_M5219, "M5219"), + DEVICE( AL, AL_M5229, "M5229 TXpro"), + DEVICE( AL, AL_M5237, "M5237 USB"), + DEVICE( SURECOM, SURECOM_NE34, "NE-34PCI LAN"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"), DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128V, "MagicGraph 128V"), + DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_128ZV, "MagicGraph 128ZV"), + DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2160, "MagicGraph NM2160"), DEVICE( ASP, ASP_ABP940, "ABP940"), DEVICE( ASP, ASP_ABP940U, "ABP940U"), + DEVICE( ASP, ASP_ABP940UW, "ABP940UW"), + DEVICE( MACRONIX, MACRONIX_MX98713,"MX98713"), + DEVICE( MACRONIX, MACRONIX_MX987x5,"MX98715 / MX98725"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( CERN, CERN_SPSB_PCI, "STAR/RD24 SCI-PCI (PMC)"), + DEVICE( CERN, CERN_HIPPI_DST, "HIPPI destination"), + DEVICE( CERN, CERN_HIPPI_SRC, "HIPPI source"), DEVICE( IMS, IMS_8849, "8849"), DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"), DEVICE( AMCC, AMCC_S5933, "S5933"), + DEVICE( AMCC, AMCC_S5933_HEPC3,"S5933 Traquair HEPC3"), DEVICE( INTERG, INTERG_1680, "IGA-1680"), DEVICE( INTERG, INTERG_1682, "IGA-1682"), DEVICE( REALTEK, REALTEK_8029, "8029"), DEVICE( REALTEK, REALTEK_8129, "8129"), + DEVICE( REALTEK, REALTEK_8139, "8139"), DEVICE( TRUEVISION, TRUEVISION_T1000,"TARGA 1000"), DEVICE( INIT, INIT_320P, "320 P"), + DEVICE( INIT, INIT_360P, "360 P"), DEVICE( VIA, VIA_82C505, "VT 82C505"), DEVICE( VIA, VIA_82C561, "VT 82C561"), - DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), + DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo IDE"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), - DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), - DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), + DEVICE( VIA, VIA_82C585, "VT 82C585 Apollo VP1/VPX"), + DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo ISA"), + DEVICE( VIA, VIA_82C595, "VT 82C595 Apollo VP2"), + DEVICE( VIA, VIA_82C597_0, "VT 82C597 Apollo VP3"), DEVICE( VIA, VIA_82C926, "VT 82C926 Amazon"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), + DEVICE( VIA, VIA_82C595_97, "VT 82C595 Apollo VP2/97"), + DEVICE( VIA, VIA_82C586_2, "VT 82C586 Apollo USB"), + 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( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), DEVICE( VORTEX, VORTEX_GDT6x10, "GDT 6110/6510"), @@ -247,31 +348,66 @@ DEVICE( VORTEX, VORTEX_GDT6x25, "GDT 6125/6525"), DEVICE( VORTEX, VORTEX_GDT6535, "GDT 6535"), DEVICE( VORTEX, VORTEX_GDT6555, "GDT 6555"), + DEVICE( VORTEX, VORTEX_GDT6x17RP,"GDT 6117RP/6517RP"), + DEVICE( VORTEX, VORTEX_GDT6x27RP,"GDT 6127RP/6527RP"), + DEVICE( VORTEX, VORTEX_GDT6537RP,"GDT 6537RP"), + DEVICE( VORTEX, VORTEX_GDT6557RP,"GDT 6557RP"), + DEVICE( VORTEX, VORTEX_GDT6x11RP,"GDT 6111RP/6511RP"), + DEVICE( VORTEX, VORTEX_GDT6x21RP,"GDT 6121RP/6521RP"), + DEVICE( VORTEX, VORTEX_GDT6x17RP1,"GDT 6117RP1/6517RP1"), + DEVICE( VORTEX, VORTEX_GDT6x27RP1,"GDT 6127RP1/6527RP1"), + DEVICE( VORTEX, VORTEX_GDT6537RP1,"GDT 6537RP1"), + DEVICE( VORTEX, VORTEX_GDT6557RP1,"GDT 6557RP1"), + DEVICE( VORTEX, VORTEX_GDT6x11RP1,"GDT 6111RP1/6511RP1"), + DEVICE( VORTEX, VORTEX_GDT6x21RP1,"GDT 6121RP1/6521RP1"), + DEVICE( VORTEX, VORTEX_GDT6x17RP2,"GDT 6117RP2/6517RP2"), + DEVICE( VORTEX, VORTEX_GDT6x27RP2,"GDT 6127RP2/6527RP2"), + DEVICE( VORTEX, VORTEX_GDT6537RP2,"GDT 6537RP2"), + DEVICE( VORTEX, VORTEX_GDT6557RP2,"GDT 6557RP2"), + DEVICE( VORTEX, VORTEX_GDT6x11RP2,"GDT 6111RP2/6511RP2"), + DEVICE( VORTEX, VORTEX_GDT6x21RP2,"GDT 6121RP2/6521RP2"), DEVICE( EF, EF_ATM_FPGA, "155P-MF1 (FPGA)"), DEVICE( EF, EF_ATM_ASIC, "155P-MF1 (ASIC)"), DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"), DEVICE( FORE, FORE_PCA200E, "PCA-200E"), DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( PHILIPS, PHILIPS_SAA7146,"SAA7146"), - DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"), + DEVICE( CYCLONE, CYCLONE_SDK, "SDK"), DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"), DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), + DEVICE( ALLIANCE, ALLIANCE_AT24, "AT24"), + DEVICE( ALLIANCE, ALLIANCE_AT3D, "AT3D"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), + DEVICE( DIGI, DIGI_EPC, "AccelPort EPC"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), + DEVICE( DIGI, DIGI_XEM, "AccelPort Xem"), + DEVICE( DIGI, DIGI_XR, "AccelPort Xr"), + DEVICE( DIGI, DIGI_CX, "AccelPort C/X"), + DEVICE( DIGI, DIGI_XRJ, "AccelPort Xr/J"), + DEVICE( DIGI, DIGI_EPCJ, "AccelPort EPC/J"), + DEVICE( DIGI, DIGI_XR_920, "AccelPort Xr 920"), DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), DEVICE( RENDITION, RENDITION_VERITE,"Verite 1000"), + DEVICE( RENDITION, RENDITION_VERITE2100,"Verite 2100"), DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), + DEVICE( TOSHIBA, TOSHIBA_TOPIC95,"ToPIC95"), + DEVICE( TOSHIBA, TOSHIBA_TOPIC97,"ToPIC97"), DEVICE( RICOH, RICOH_RL5C466, "RL5C466"), + DEVICE( ARTOP, ARTOP_ATP850UF, "ATP850UF"), DEVICE( ZEITNET, ZEITNET_1221, "1221"), DEVICE( ZEITNET, ZEITNET_1225, "1225"), DEVICE( OMEGA, OMEGA_82C092G, "82C092G"), + DEVICE( LITEON, LITEON_LNE100TX,"LNE100TX"), DEVICE( NP, NP_PCI_FDDI, "NP-PCI"), + DEVICE( ATT, ATT_L56XMF, "L56xMF"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( AURAVISION, AURAVISION_VXP524,"VXP524"), DEVICE( IKON, IKON_10115, "10115 Greensheet"), DEVICE( IKON, IKON_10117, "10117 Greensheet"), DEVICE( ZORAN, ZORAN_36057, "ZR36057"), DEVICE( ZORAN, ZORAN_36120, "ZR36120"), + DEVICE( KINETIC, KINETIC_2915, "2915 CAMAC"), DEVICE( COMPEX, COMPEX_ENET100VG4, "Readylink ENET100-VG4"), DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"), DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"), @@ -282,19 +418,32 @@ DEVICE( CYCLADES, CYCLOM_Y_Hi, "Cyclom-Y above 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Hi, "Cyclom-Z above 1Mbyte"), + DEVICE( ESSENTIAL, ESSENTIAL_ROADRUNNER,"Roadrunner serial HIPPI"), + DEVICE( O2, O2_6832, "6832"), DEVICE( 3DFX, 3DFX_VOODOO, "Voodoo"), + DEVICE( 3DFX, 3DFX_VOODOO2, "Voodoo2"), DEVICE( SIGMADES, SIGMADES_6425, "REALmagic64/GX"), + DEVICE( STALLION, STALLION_ECHPCI832,"EasyConnection 8/32"), + DEVICE( STALLION, STALLION_ECHPCI864,"EasyConnection 8/64"), + DEVICE( STALLION, STALLION_EIOPCI,"EasyIO"), DEVICE( OPTIBASE, OPTIBASE_FORGE, "MPEG Forge"), DEVICE( OPTIBASE, OPTIBASE_FUSION,"MPEG Fusion"), DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"), DEVICE( OPTIBASE, OPTIBASE_VPLEXCC,"VideoPlex CC"), DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"), + DEVICE( SATSAGEM, SATSAGEM_PCR2101,"PCR2101 DVB receiver"), + DEVICE( SATSAGEM, SATSAGEM_TELSATTURBO,"Telsat Turbo DVB"), + DEVICE( ENSONIQ, ENSONIQ_AUDIOPCI,"AudioPCI"), + DEVICE( PICTUREL, PICTUREL_PCIVST,"PCIVST"), + DEVICE( NVIDIA_SGS, NVIDIA_SGS_RIVA128, "Riva 128"), + DEVICE( CBOARDS, CBOARDS_DAS1602_16,"DAS1602/16"), DEVICE( SYMPHONY, SYMPHONY_101, "82C101"), DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"), DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"), DEVICE( 3DLABS, 3DLABS_500TX, "GLINT 500TX"), DEVICE( 3DLABS, 3DLABS_DELTA, "GLINT Delta"), DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"), + DEVICE( 3DLABS, 3DLABS_MX, "GLINT MX"), DEVICE( AVANCE, AVANCE_ALG2064, "ALG2064i"), DEVICE( AVANCE, AVANCE_2302, "ALG-2302"), DEVICE( NETVIN, NETVIN_NV5000SC,"NV5000"), @@ -315,6 +464,10 @@ DEVICE( S3, S3_PLATO_PXG, "PLATO/PX (graphics)"), DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"), DEVICE( S3, S3_ViRGE_GX2, "ViRGE/GX2"), + DEVICE( S3, S3_ViRGE_MX, "ViRGE/MX"), + DEVICE( S3, S3_ViRGE_MXP, "ViRGE/MX+"), + DEVICE( S3, S3_ViRGE_MXPMV, "ViRGE/MX+MV"), + DEVICE( S3, S3_SONICVIBES, "SonicVibes"), DEVICE( INTEL, INTEL_82375, "82375EB"), BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), DEVICE( INTEL, INTEL_82378, "82378IB"), @@ -332,21 +485,29 @@ DEVICE( INTEL, INTEL_82371MX, "430MX - 82371MX MPIIX"), DEVICE( INTEL, INTEL_82437MX, "430MX - 82437MX MTSC"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), + DEVICE( INTEL, INTEL_82380FB, "82380FB Mobile"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), - DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), - DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), - DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), + DEVICE( INTEL, INTEL_82371SB_0,"82371SB PIIX3 ISA"), + DEVICE( INTEL, INTEL_82371SB_1,"82371SB PIIX3 IDE"), + DEVICE( INTEL, INTEL_82371SB_2,"82371SB PIIX3 USB"), DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), DEVICE( INTEL, INTEL_82439TX, "82439TX"), - DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4"), - DEVICE( INTEL, INTEL_82371AB, "82371AB 430TX PIIX4"), - DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4"), - DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 Power Management"), + DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4 ISA"), + DEVICE( INTEL, INTEL_82371AB, "82371AB PIIX4 IDE"), + DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4 USB"), + DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 ACPI"), + DEVICE( INTEL, INTEL_82443LX_0,"440LX - 82443LX PAC Host"), + DEVICE( INTEL, INTEL_82443LX_1,"440LX - 82443LX PAC AGP"), + DEVICE( INTEL, INTEL_82443BX_0,"440BX - 82443BX Host"), + DEVICE( INTEL, INTEL_82443BX_1,"440BX - 82443BX AGP"), + DEVICE( INTEL, INTEL_82443BX_2,"440BX - 82443BX Host (no AGP)"), DEVICE( INTEL, INTEL_P6, "Orion P6"), DEVICE( INTEL, INTEL_82450GX, "82450GX Orion P6"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), + DEVICE( ADAPTEC, ADAPTEC_7810, "AIC-7810 RAID"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), + DEVICE( ADAPTEC, ADAPTEC_5800, "AIC-5800"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), DEVICE( ADAPTEC, ADAPTEC_7861, "AIC-7861"), DEVICE( ADAPTEC, ADAPTEC_7870, "AIC-7870"), @@ -354,12 +515,15 @@ DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"), DEVICE( ADAPTEC, ADAPTEC_7873, "AIC-7873"), DEVICE( ADAPTEC, ADAPTEC_7874, "AIC-7874"), + DEVICE( ADAPTEC, ADAPTEC_7895, "AIC-7895U"), DEVICE( ADAPTEC, ADAPTEC_7880, "AIC-7880U"), DEVICE( ADAPTEC, ADAPTEC_7881, "AIC-7881U"), DEVICE( ADAPTEC, ADAPTEC_7882, "AIC-7882U"), DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), + DEVICE( ADAPTEC, ADAPTEC_1030, "ABA-1030 DVB receiver"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), + DEVICE( TIGERJET, TIGERJET_300, "Tiger300 ISDN"), DEVICE( ARK, ARK_STING, "Stingray"), DEVICE( ARK, ARK_STINGARK, "Stingray ARK 2000PV"), DEVICE( ARK, ARK_2000MT, "2000MT") @@ -576,6 +740,7 @@ case PCI_VENDOR_ID_MATROX: return "Matrox"; case PCI_VENDOR_ID_CT: return "Chips & Technologies"; case PCI_VENDOR_ID_MIRO: return "Miro"; + case PCI_VENDOR_ID_NEC: return "NEC"; case PCI_VENDOR_ID_FD: return "Future Domain"; case PCI_VENDOR_ID_SI: return "Silicon Integrated Systems"; case PCI_VENDOR_ID_HP: return "Hewlett Packard"; @@ -728,15 +893,16 @@ static int sprint_dev_config(struct pci_dev *dev, char *buf, int size) { unsigned long base; - unsigned int l, class_rev, bus, devfn; + unsigned int l, class_rev, bus, devfn, last_reg; unsigned short vendor, device, status; - unsigned char bist, latency, min_gnt, max_lat; + unsigned char bist, latency, min_gnt, max_lat, hdr_type; int reg, len = 0; const char *str; bus = dev->bus->number; devfn = dev->devfn; + pcibios_read_config_byte (bus, devfn, PCI_HEADER_TYPE, &hdr_type); pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class_rev); pcibios_read_config_word (bus, devfn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word (bus, devfn, PCI_DEVICE_ID, &device); @@ -815,7 +981,17 @@ len += sprintf(buf + len, "Max Lat=%d.", max_lat); } - for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + switch (hdr_type & 0x7f) { + case 0: + last_reg = PCI_BASE_ADDRESS_5; + break; + case 1: + last_reg = PCI_BASE_ADDRESS_1; + break; + default: + last_reg = 0; + } + for (reg = PCI_BASE_ADDRESS_0; reg <= last_reg; reg += 4) { if (len + 40 > size) { return -1; } @@ -908,7 +1084,7 @@ static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp) { unsigned int devfn, l, max; - unsigned char cmd, tmp, hdr_type = 0; + unsigned char cmd, tmp, hdr_type, ht, is_multi = 0; struct pci_dev_info *info; struct pci_dev *dev; struct pci_bus *child; @@ -919,32 +1095,23 @@ max = bus->secondary; for (devfn = 0; devfn < 0xff; ++devfn) { - if (PCI_FUNC(devfn) == 0) { - pcibios_read_config_byte(bus->number, devfn, - PCI_HEADER_TYPE, &hdr_type); - } else if (!(hdr_type & 0x80)) { - /* not a multi-function device */ + if (PCI_FUNC(devfn) && !is_multi) { + /* Not a multi-function device */ continue; } + pcibios_read_config_byte(bus->number, devfn, PCI_HEADER_TYPE, &hdr_type); + if (!PCI_FUNC(devfn)) + is_multi = hdr_type & 0x80; - pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, - &l); + pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID, &l); /* some broken boards return 0 if a slot is empty: */ if (l == 0xffffffff || l == 0x00000000) { - hdr_type = 0; + is_multi = 0; continue; } dev = pci_malloc(sizeof(*dev), mem_startp); dev->bus = bus; - /* - * Put it into the simple chain of devices on this - * bus. It is used to find devices once everything is - * set up. - */ - dev->next = pci_devices; - pci_devices = dev; - dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; @@ -956,8 +1123,10 @@ */ info = pci_lookup_dev(dev->vendor, dev->device); if (!info) { - printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h \n", +#if 0 + printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h\n", dev->vendor, dev->device); +#endif } else { /* Some BIOS' are lazy. Let's do their job: */ if (info->bridge_type != 0xff) { @@ -986,6 +1155,35 @@ PCI_CLASS_REVISION, &l); l = l >> 8; /* upper 3 bytes */ dev->class = l; + + /* + * Check if the header type is known and consistent with + * device type. PCI-to-PCI Bridges should have hdr_type 1, + * CardBus Bridges 2, all other devices 0. + */ + switch (dev->class >> 8) { + case PCI_CLASS_BRIDGE_PCI: + ht = 1; + break; + case PCI_CLASS_BRIDGE_CARDBUS: + ht = 2; + break; + default: + ht = 0; + } + if (ht != (hdr_type & 0x7f)) { + printk(KERN_WARNING "PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n", + bus->number, dev->devfn, dev->vendor, dev->device, dev->class, hdr_type); + continue; + } + + /* + * Put it into the simple chain of all PCI devices. + * It is used to find devices once everything is set up. + */ + dev->next = pci_devices; + pci_devices = dev; + /* * Now insert it into the list of devices held * by the parent bus. diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c --- v2.0.33/linux/drivers/scsi/BusLogic.c Mon Aug 11 00:10:00 1997 +++ linux/drivers/scsi/BusLogic.c Wed Jun 3 15:17:48 1998 @@ -2,12 +2,11 @@ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters - Copyright 1995 by Leonard N. Zubkoff + Copyright 1995-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, provided that none of the source code or runtime - copyright notices are removed or modified. + 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 @@ -27,17 +26,17 @@ */ -#define BusLogic_DriverVersion "2.0.10" -#define BusLogic_DriverDate "11 August 1997" +#define BusLogic_DriverVersion "2.0.14" +#define BusLogic_DriverDate "29 April 1998" +#include #include #include #include #include #include #include -#include #include #include #include @@ -45,30 +44,43 @@ #include #include #include +#include #include #include "scsi.h" #include "hosts.h" #include "sd.h" #include "BusLogic.h" +#include "FlashPoint.c" /* - BusLogic_CommandLineEntryCount is a count of the number of "BusLogic=" - entries provided on the Linux Kernel Command Line. + BusLogic_DriverOptionsCount is a count of the number of BusLogic Driver + Options specifications provided via the Linux Kernel Command Line or via + the Loadable Kernel Module Installation Facility. */ static int - BusLogic_CommandLineEntryCount = 0; + BusLogic_DriverOptionsCount = 0; /* - BusLogic_CommandLineEntries is an array of Command Line Entry structures - representing the "BusLogic=" entries provided on the Linux Kernel Command - Line. + BusLogic_DriverOptions is an array of Driver Options structures representing + BusLogic Driver Options specifications provided via the Linux Kernel Command + Line or via the Loadable Kernel Module Installation Facility. */ -static BusLogic_CommandLineEntry_T - BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters]; +static BusLogic_DriverOptions_T + BusLogic_DriverOptions[BusLogic_MaxHostAdapters]; + + +/* + BusLogic_Options can be assigned a string by the Loadable Kernel Module + Installation Facility to be parsed for BusLogic Driver Options + specifications. +*/ + +static char + *BusLogic_Options = NULL; /* @@ -77,7 +89,7 @@ */ static BusLogic_ProbeOptions_T - BusLogic_ProbeOptions = { 0 }; + BusLogic_ProbeOptions = { 0 }; /* @@ -86,24 +98,25 @@ */ static BusLogic_GlobalOptions_T - BusLogic_GlobalOptions = { 0 }; + BusLogic_GlobalOptions = { 0 }; /* - BusLogic_RegisteredHostAdapters is an array of linked lists of all the - registered BusLogic Host Adapters, indexed by IRQ Channel. + BusLogic_FirstRegisteredHostAdapter and BusLogic_LastRegisteredHostAdapter + are pointers to the first and last registered BusLogic Host Adapters. */ static BusLogic_HostAdapter_T - *BusLogic_RegisteredHostAdapters[NR_IRQS] = { NULL }; + *BusLogic_FirstRegisteredHostAdapter = NULL, + *BusLogic_LastRegisteredHostAdapter = NULL; /* - BusLogic_ProbeInfoCount is the numbers of entries in BusLogic_ProbeInfoList. + BusLogic_ProbeInfoCount is the number of entries in BusLogic_ProbeInfoList. */ static int - BusLogic_ProbeInfoCount = 0; + BusLogic_ProbeInfoCount = 0; /* @@ -114,7 +127,7 @@ */ static BusLogic_ProbeInfo_T - BusLogic_ProbeInfoList[BusLogic_MaxHostAdapters] = { { 0 } }; + *BusLogic_ProbeInfoList = NULL; /* @@ -128,16 +141,6 @@ /* - BusLogic_FirstCompletedCCB and BusLogic_LastCompletedCCB are pointers - to the first and last CCBs that are queued for completion processing. -*/ - -static BusLogic_CCB_T - *BusLogic_FirstCompletedCCB = NULL, - *BusLogic_LastCompletedCCB = NULL; - - -/* BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry. */ @@ -156,7 +159,7 @@ BusLogic_Announce("***** BusLogic SCSI Driver Version " BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n", HostAdapter); - BusLogic_Announce("Copyright 1995 by Leonard N. Zubkoff " + BusLogic_Announce("Copyright 1995-1998 by Leonard N. Zubkoff " "\n", HostAdapter); } @@ -182,16 +185,16 @@ static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) { HostAdapter->Next = NULL; - if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != NULL) + if (BusLogic_FirstRegisteredHostAdapter == NULL) + { + BusLogic_FirstRegisteredHostAdapter = HostAdapter; + BusLogic_LastRegisteredHostAdapter = HostAdapter; + } + else { - BusLogic_HostAdapter_T *LastHostAdapter = - BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel]; - BusLogic_HostAdapter_T *NextHostAdapter; - while ((NextHostAdapter = LastHostAdapter->Next) != NULL) - LastHostAdapter = NextHostAdapter; - LastHostAdapter->Next = HostAdapter; + BusLogic_LastRegisteredHostAdapter->Next = HostAdapter; + BusLogic_LastRegisteredHostAdapter = HostAdapter; } - else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] = HostAdapter; } @@ -202,101 +205,55 @@ static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter) { - if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != HostAdapter) + if (HostAdapter == BusLogic_FirstRegisteredHostAdapter) { - BusLogic_HostAdapter_T *LastHostAdapter = - BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel]; - while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter) - LastHostAdapter = LastHostAdapter->Next; - if (LastHostAdapter != NULL) - LastHostAdapter->Next = HostAdapter->Next; + BusLogic_FirstRegisteredHostAdapter = + BusLogic_FirstRegisteredHostAdapter->Next; + if (HostAdapter == BusLogic_LastRegisteredHostAdapter) + BusLogic_LastRegisteredHostAdapter = NULL; } - else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] = - HostAdapter->Next; - HostAdapter->Next = NULL; -} - - -/* - BusLogic_CreateMailboxes allocates the Outgoing and Incoming Mailboxes for - Host Adapter. -*/ - -static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter) -{ - /* - FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes. - */ - if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true; - /* - Allocate space for the Outgoing and Incoming Mailboxes. - */ - HostAdapter->FirstOutgoingMailbox = - (BusLogic_OutgoingMailbox_T *) - scsi_init_malloc(HostAdapter->MailboxCount - * (sizeof(BusLogic_OutgoingMailbox_T) - + sizeof(BusLogic_IncomingMailbox_T)), - (HostAdapter->BounceBuffersRequired - ? GFP_ATOMIC | GFP_DMA - : GFP_ATOMIC)); - if (HostAdapter->FirstOutgoingMailbox == NULL) + else { - BusLogic_Error("UNABLE TO ALLOCATE MAILBOXES - DETACHING\n", - HostAdapter, HostAdapter->HostNumber); - return false; + BusLogic_HostAdapter_T *PreviousHostAdapter = + BusLogic_FirstRegisteredHostAdapter; + while (PreviousHostAdapter != NULL && + PreviousHostAdapter->Next != HostAdapter) + PreviousHostAdapter = PreviousHostAdapter->Next; + if (PreviousHostAdapter != NULL) + PreviousHostAdapter->Next = HostAdapter->Next; } - HostAdapter->LastOutgoingMailbox = - HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1; - HostAdapter->FirstIncomingMailbox = - (BusLogic_IncomingMailbox_T *) (HostAdapter->LastOutgoingMailbox + 1); - HostAdapter->LastIncomingMailbox = - HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1; - return true; -} - - -/* - BusLogic_DestroyMailboxes deallocates the Outgoing and Incoming Mailboxes - for Host Adapter. -*/ - -static void BusLogic_DestroyMailboxes(BusLogic_HostAdapter_T *HostAdapter) -{ - if (HostAdapter->FirstOutgoingMailbox == NULL) return; - scsi_init_free((char *) HostAdapter->FirstOutgoingMailbox, - HostAdapter->MailboxCount - * (sizeof(BusLogic_OutgoingMailbox_T) - + sizeof(BusLogic_IncomingMailbox_T))); + HostAdapter->Next = NULL; } /* - BusLogic_CreateCCB allocates and initializes a single Command Control - Block (CCB) for Host Adapter, and adds it to Host Adapter's free list. -*/ - -static boolean BusLogic_CreateCCB(BusLogic_HostAdapter_T *HostAdapter) -{ - BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) - scsi_init_malloc(sizeof(BusLogic_CCB_T), - (HostAdapter->BounceBuffersRequired - ? GFP_ATOMIC | GFP_DMA - : GFP_ATOMIC)); - if (CCB == NULL) return false; - memset(CCB, 0, sizeof(BusLogic_CCB_T)); - CCB->HostAdapter = HostAdapter; - CCB->Status = BusLogic_CCB_Free; - if (BusLogic_FlashPointHostAdapterP(HostAdapter)) - { - CCB->CallbackFunction = BusLogic_QueueCompletedCCB; - CCB->BaseAddress = HostAdapter->IO_Address; + BusLogic_InitializeCCBs initializes a group of Command Control Blocks (CCBs) + for Host Adapter from the BlockSize bytes located at BlockPointer. The newly + created CCBs are added to Host Adapter's free list. +*/ + +static void BusLogic_InitializeCCBs(BusLogic_HostAdapter_T *HostAdapter, + void *BlockPointer, int BlockSize) +{ + BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) BlockPointer; + memset(BlockPointer, 0, BlockSize); + CCB->AllocationGroupHead = true; + while ((BlockSize -= sizeof(BusLogic_CCB_T)) >= 0) + { + CCB->Status = BusLogic_CCB_Free; + CCB->HostAdapter = HostAdapter; + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + { + CCB->CallbackFunction = BusLogic_QueueCompletedCCB; + CCB->BaseAddress = HostAdapter->FlashPointInfo.BaseAddress; + } + CCB->Next = HostAdapter->Free_CCBs; + CCB->NextAll = HostAdapter->All_CCBs; + HostAdapter->Free_CCBs = CCB; + HostAdapter->All_CCBs = CCB; + HostAdapter->AllocatedCCBs++; + CCB++; } - CCB->Next = HostAdapter->Free_CCBs; - CCB->NextAll = HostAdapter->All_CCBs; - HostAdapter->Free_CCBs = CCB; - HostAdapter->All_CCBs = CCB; - HostAdapter->AllocatedCCBs++; - return true; } @@ -306,14 +263,21 @@ static boolean BusLogic_CreateInitialCCBs(BusLogic_HostAdapter_T *HostAdapter) { - int Allocated; - for (Allocated = 0; Allocated < HostAdapter->InitialCCBs; Allocated++) - if (!BusLogic_CreateCCB(HostAdapter)) - { - BusLogic_Error("UNABLE TO ALLOCATE CCB %d - DETACHING\n", - HostAdapter, Allocated); - return false; - } + int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(BusLogic_CCB_T); + while (HostAdapter->AllocatedCCBs < HostAdapter->InitialCCBs) + { + void *BlockPointer = kmalloc(BlockSize, + (HostAdapter->BounceBuffersRequired + ? GFP_ATOMIC | GFP_DMA + : GFP_ATOMIC)); + if (BlockPointer == NULL) + { + BusLogic_Error("UNABLE TO ALLOCATE CCB GROUP - DETACHING\n", + HostAdapter); + return false; + } + BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize); + } return true; } @@ -330,7 +294,8 @@ while ((CCB = NextCCB) != NULL) { NextCCB = CCB->NextAll; - scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T)); + if (CCB->AllocationGroupHead) + kfree(CCB); } } @@ -346,18 +311,35 @@ int AdditionalCCBs, boolean SuccessMessageP) { - int Allocated; + int BlockSize = BusLogic_CCB_AllocationGroupSize * sizeof(BusLogic_CCB_T); + int PreviouslyAllocated = HostAdapter->AllocatedCCBs; if (AdditionalCCBs <= 0) return; - for (Allocated = 0; Allocated < AdditionalCCBs; Allocated++) - if (!BusLogic_CreateCCB(HostAdapter)) break; - if (Allocated > 0 && SuccessMessageP) - BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n", - HostAdapter, Allocated, HostAdapter->AllocatedCCBs); - if (Allocated > 0) return; + while (HostAdapter->AllocatedCCBs - PreviouslyAllocated < AdditionalCCBs) + { + void *BlockPointer = kmalloc(BlockSize, + (HostAdapter->BounceBuffersRequired + ? GFP_ATOMIC | GFP_DMA + : GFP_ATOMIC)); + if (BlockPointer == NULL) break; + BusLogic_InitializeCCBs(HostAdapter, BlockPointer, BlockSize); + } + if (HostAdapter->AllocatedCCBs > PreviouslyAllocated) + { + if (SuccessMessageP) + BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n", + HostAdapter, + HostAdapter->AllocatedCCBs - PreviouslyAllocated, + HostAdapter->AllocatedCCBs); + return; + } BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter); - HostAdapter->DriverQueueDepth = - HostAdapter->AllocatedCCBs - (HostAdapter->MaxTargetDevices - 1); - HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth; + if (HostAdapter->DriverQueueDepth > + HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount) + { + HostAdapter->DriverQueueDepth = + HostAdapter->AllocatedCCBs - HostAdapter->TargetDeviceCount; + HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth; + } } @@ -413,47 +395,6 @@ /* - BusLogic_CreateTargetDeviceStatistics creates the Target Device Statistics - structure for Host Adapter. -*/ - -static boolean BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T - *HostAdapter) -{ - HostAdapter->TargetDeviceStatistics = - (BusLogic_TargetDeviceStatistics_T *) - scsi_init_malloc(HostAdapter->MaxTargetDevices - * sizeof(BusLogic_TargetDeviceStatistics_T), - GFP_ATOMIC); - if (HostAdapter->TargetDeviceStatistics == NULL) - { - BusLogic_Error("UNABLE TO ALLOCATE TARGET DEVICE STATISTICS - " - "DETACHING\n", HostAdapter, HostAdapter->HostNumber); - return false; - } - memset(HostAdapter->TargetDeviceStatistics, 0, - HostAdapter->MaxTargetDevices - * sizeof(BusLogic_TargetDeviceStatistics_T)); - return true; -} - - -/* - BusLogic_DestroyTargetDeviceStatistics destroys the Target Device Statistics - structure for Host Adapter. -*/ - -static void BusLogic_DestroyTargetDeviceStatistics(BusLogic_HostAdapter_T - *HostAdapter) -{ - if (HostAdapter->TargetDeviceStatistics == NULL) return; - scsi_init_free((char *) HostAdapter->TargetDeviceStatistics, - HostAdapter->MaxTargetDevices - * sizeof(BusLogic_TargetDeviceStatistics_T)); -} - - -/* BusLogic_Command sends the command OperationCode to HostAdapter, optionally providing ParameterLength bytes of ParameterData and receiving at most ReplyLength bytes of ReplyData; any excess reply data is received but @@ -482,7 +423,7 @@ unsigned char *ReplyPointer = (unsigned char *) ReplyData; BusLogic_StatusRegister_T StatusRegister; BusLogic_InterruptRegister_T InterruptRegister; - unsigned long ProcessorFlags = 0; + ProcessorFlags_T ProcessorFlags = 0; int ReplyBytes = 0, Result; long TimeoutCounter; /* @@ -494,7 +435,7 @@ If the IRQ Channel has not yet been acquired, then interrupts must be disabled while issuing host adapter commands since a Command Complete interrupt could occur if the IRQ Channel was previously enabled by another - BusLogic Host Adapter or other driver sharing the same IRQ Channel. + BusLogic Host Adapter or another driver sharing the same IRQ Channel. */ if (!HostAdapter->IRQ_ChannelAcquired) { @@ -572,7 +513,7 @@ Result = -1; goto Done; } - if (BusLogic_GlobalOptions.Bits.TraceConfiguration) + if (BusLogic_GlobalOptions.TraceConfiguration) BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: " "(Modify I/O Address)\n", HostAdapter, OperationCode, StatusRegister.All); @@ -607,9 +548,11 @@ if (InterruptRegister.Bits.CommandComplete) break; if (HostAdapter->HostAdapterCommandCompleted) break; if (StatusRegister.Bits.DataInRegisterReady) - if (++ReplyBytes <= ReplyLength) - *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter); - else BusLogic_ReadDataInRegister(HostAdapter); + { + if (++ReplyBytes <= ReplyLength) + *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter); + else BusLogic_ReadDataInRegister(HostAdapter); + } if (OperationCode == BusLogic_FetchHostAdapterLocalRAM && StatusRegister.Bits.HostAdapterReady) break; udelay(100); @@ -621,34 +564,24 @@ goto Done; } /* - If testing Command Complete Interrupts, wait a short while in case the - loop immediately above terminated due to the Command Complete bit being - set in the Interrupt Register, but the interrupt hasn't actually been - processed yet. Otherwise, acknowledging the interrupt here could prevent - the interrupt test from succeeding. - */ - if (OperationCode == BusLogic_TestCommandCompleteInterrupt) - udelay(10000); - /* Clear any pending Command Complete Interrupt. */ BusLogic_InterruptReset(HostAdapter); /* Provide tracing information if requested. */ - if (BusLogic_GlobalOptions.Bits.TraceConfiguration) - if (OperationCode != BusLogic_TestCommandCompleteInterrupt) - { - int i; - BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", - HostAdapter, OperationCode, - StatusRegister.All, ReplyLength, ReplyBytes); - if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes; - for (i = 0; i < ReplyLength; i++) - BusLogic_Notice(" %02X", HostAdapter, - ((unsigned char *) ReplyData)[i]); - BusLogic_Notice("\n", HostAdapter); - } + if (BusLogic_GlobalOptions.TraceConfiguration) + { + int i; + BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:", + HostAdapter, OperationCode, + StatusRegister.All, ReplyLength, ReplyBytes); + if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes; + for (i = 0; i < ReplyLength; i++) + BusLogic_Notice(" %02X", HostAdapter, + ((unsigned char *) ReplyData)[i]); + BusLogic_Notice("\n", HostAdapter); + } /* Process Command Invalid conditions. */ @@ -705,38 +638,63 @@ /* + BusLogic_AppendProbeAddressISA appends a single ISA I/O Address to the list + of I/O Address and Bus Probe Information to be checked for potential BusLogic + Host Adapters. +*/ + +static void BusLogic_AppendProbeAddressISA(BusLogic_IO_Address_T IO_Address) +{ + BusLogic_ProbeInfo_T *ProbeInfo; + if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) return; + ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; + ProbeInfo->HostAdapterType = BusLogic_MultiMaster; + ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; + ProbeInfo->IO_Address = IO_Address; +} + + +/* BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters only from the list of standard BusLogic MultiMaster ISA I/O Addresses. */ -static void BusLogic_InitializeProbeInfoListISA(void) +static void BusLogic_InitializeProbeInfoListISA(BusLogic_HostAdapter_T + *PrototypeHostAdapter) { - int StandardAddressIndex; - /* - If BusLogic_Setup has provided an I/O Address probe list, do not override - the Kernel Command Line specifications. - */ - if (BusLogic_ProbeInfoCount > 0) return; /* - If a Kernel Command Line specification has requested that ISA Bus Probes + If BusLogic Driver Options specifications requested that ISA Bus Probes be inhibited, do not proceed further. */ - if (BusLogic_ProbeOptions.Bits.NoProbeISA) return; + if (BusLogic_ProbeOptions.NoProbeISA) return; /* Append the list of standard BusLogic MultiMaster ISA I/O Addresses. */ - StandardAddressIndex = 0; - while (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters && - StandardAddressIndex < BusLogic_ISA_StandardAddressesCount) - { - BusLogic_ProbeInfo_T *ProbeInfo = - &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; - ProbeInfo->IO_Address = - BusLogic_ISA_StandardAddresses[StandardAddressIndex++]; - ProbeInfo->HostAdapterType = BusLogic_MultiMaster; - ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; - } + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe330 + : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x330); + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe334 + : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x334); + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe230 + : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x230); + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe234 + : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x234); + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe130 + : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x130); + if (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe134 + : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0) + BusLogic_AppendProbeAddressISA(0x134); } @@ -783,25 +741,26 @@ I/O Addresses. It returns the number of PCI MultiMaster Host Adapters found. */ -static int BusLogic_InitializeMultiMasterProbeInfo(void) +static int BusLogic_InitializeMultiMasterProbeInfo(BusLogic_HostAdapter_T + *PrototypeHostAdapter) { - boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount]; BusLogic_ProbeInfo_T *PrimaryProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount]; int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount + 1; int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0; boolean ForceBusDeviceScanningOrder = false; boolean ForceBusDeviceScanningOrderChecked = false; - unsigned char Bus, DeviceFunction, IRQ_Channel; + boolean StandardAddressSeen[6]; + unsigned char Bus, DeviceFunction; unsigned int BaseAddress0, BaseAddress1; + unsigned char IRQ_Channel; BusLogic_IO_Address_T IO_Address; BusLogic_PCI_Address_T PCI_Address; unsigned short Index = 0; - int StandardAddressIndex, i; - if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) - return 0; + int i; + if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters) return 0; BusLogic_ProbeInfoCount++; - for (i = 0; i < BusLogic_ISA_StandardAddressesCount; i++) + for (i = 0; i < 6; i++) StandardAddressSeen[i] = false; /* Iterate over the MultiMaster PCI Host Adapters. For each enumerated host @@ -826,8 +785,7 @@ pcibios_read_config_byte(Bus, DeviceFunction, PCI_INTERRUPT_LINE, &IRQ_Channel) == 0) { - BusLogic_HostAdapter_T HostAdapterPrototype; - BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter; BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation; BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest; unsigned char Device = DeviceFunction >> 3; @@ -859,7 +817,7 @@ NULL, Bus, Device, IO_Address); continue; } - if (BusLogic_GlobalOptions.Bits.TraceProbe) + if (BusLogic_GlobalOptions.TraceProbe) { BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter " "detected at\n", NULL); @@ -871,17 +829,17 @@ Issue the Inquire PCI Host Adapter Information command to determine the ISA Compatible I/O Port. If the ISA Compatible I/O Port is known and enabled, note that the particular Standard ISA I/O - Address need not be probed. + Address should not be probed. */ HostAdapter->IO_Address = IO_Address; + BusLogic_InterruptReset(HostAdapter); if (BusLogic_Command(HostAdapter, BusLogic_InquirePCIHostAdapterInformation, NULL, 0, &PCIHostAdapterInformation, sizeof(PCIHostAdapterInformation)) == sizeof(PCIHostAdapterInformation)) { - if (PCIHostAdapterInformation.ISACompatibleIOPort < - BusLogic_ISA_StandardAddressesCount) + if (PCIHostAdapterInformation.ISACompatibleIOPort < 6) StandardAddressSeen[PCIHostAdapterInformation .ISACompatibleIOPort] = true; } @@ -933,10 +891,10 @@ */ if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330) { - PrimaryProbeInfo->IO_Address = IO_Address; - PrimaryProbeInfo->PCI_Address = PCI_Address; PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster; PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus; + PrimaryProbeInfo->IO_Address = IO_Address; + PrimaryProbeInfo->PCI_Address = PCI_Address; PrimaryProbeInfo->Bus = Bus; PrimaryProbeInfo->Device = Device; PrimaryProbeInfo->IRQ_Channel = IRQ_Channel; @@ -946,10 +904,10 @@ { BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; - ProbeInfo->IO_Address = IO_Address; - ProbeInfo->PCI_Address = PCI_Address; ProbeInfo->HostAdapterType = BusLogic_MultiMaster; ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus; + ProbeInfo->IO_Address = IO_Address; + ProbeInfo->PCI_Address = PCI_Address; ProbeInfo->Bus = Bus; ProbeInfo->Device = Device; ProbeInfo->IRQ_Channel = IRQ_Channel; @@ -978,31 +936,80 @@ then the Primary I/O Address must be probed explicitly before any PCI host adapters are probed. */ - if (PrimaryProbeInfo->IO_Address == 0 && - !BusLogic_ProbeOptions.Bits.NoProbeISA) - { - PrimaryProbeInfo->IO_Address = BusLogic_ISA_StandardAddresses[0]; - PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster; - PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; - } + if (!BusLogic_ProbeOptions.NoProbeISA) + if (PrimaryProbeInfo->IO_Address == 0 && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe330 + : check_region(0x330, BusLogic_MultiMasterAddressCount) == 0)) + { + PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster; + PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; + PrimaryProbeInfo->IO_Address = 0x330; + } /* Append the list of standard BusLogic MultiMaster ISA I/O Addresses, omitting the Primary I/O Address which has already been handled. */ - if (!BusLogic_ProbeOptions.Bits.NoProbeISA) - for (StandardAddressIndex = 1; - StandardAddressIndex < BusLogic_ISA_StandardAddressesCount; - StandardAddressIndex++) - if (!StandardAddressSeen[StandardAddressIndex] && - BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters) - { - BusLogic_ProbeInfo_T *ProbeInfo = - &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; - ProbeInfo->IO_Address = - BusLogic_ISA_StandardAddresses[StandardAddressIndex]; - ProbeInfo->HostAdapterType = BusLogic_MultiMaster; - ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; - } + if (!BusLogic_ProbeOptions.NoProbeISA) + { + if (!StandardAddressSeen[1] && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe334 + : check_region(0x334, BusLogic_MultiMasterAddressCount) == 0)) + BusLogic_AppendProbeAddressISA(0x334); + if (!StandardAddressSeen[2] && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe230 + : check_region(0x230, BusLogic_MultiMasterAddressCount) == 0)) + BusLogic_AppendProbeAddressISA(0x230); + if (!StandardAddressSeen[3] && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe234 + : check_region(0x234, BusLogic_MultiMasterAddressCount) == 0)) + BusLogic_AppendProbeAddressISA(0x234); + if (!StandardAddressSeen[4] && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe130 + : check_region(0x130, BusLogic_MultiMasterAddressCount) == 0)) + BusLogic_AppendProbeAddressISA(0x130); + if (!StandardAddressSeen[5] && + (BusLogic_ProbeOptions.LimitedProbeISA + ? BusLogic_ProbeOptions.Probe134 + : check_region(0x134, BusLogic_MultiMasterAddressCount) == 0)) + BusLogic_AppendProbeAddressISA(0x134); + } + /* + Iterate over the older non-compliant MultiMaster PCI Host Adapters, + noting the PCI bus location and assigned IRQ Channel. + */ + Index = 0; + while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC, + PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC, + Index++, &Bus, &DeviceFunction) == 0) + if (pcibios_read_config_dword(Bus, DeviceFunction, + PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 && + pcibios_read_config_byte(Bus, DeviceFunction, + PCI_INTERRUPT_LINE, &IRQ_Channel) == 0) + { + unsigned char Device = DeviceFunction >> 3; + IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK; + if (IO_Address == 0 || IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS) + continue; + for (i = 0; i < BusLogic_ProbeInfoCount; i++) + { + BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[i]; + if (ProbeInfo->IO_Address == IO_Address && + ProbeInfo->HostAdapterType == BusLogic_MultiMaster) + { + ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus; + ProbeInfo->PCI_Address = 0; + ProbeInfo->Bus = Bus; + ProbeInfo->Device = Device; + ProbeInfo->IRQ_Channel = IRQ_Channel; + break; + } + } + } return PCIMultiMasterCount; } @@ -1014,11 +1021,13 @@ number of FlashPoint Host Adapters found. */ -static int BusLogic_InitializeFlashPointProbeInfo(void) +static int BusLogic_InitializeFlashPointProbeInfo(BusLogic_HostAdapter_T + *PrototypeHostAdapter) { int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0; - unsigned char Bus, DeviceFunction, IRQ_Channel; + unsigned char Bus, DeviceFunction; unsigned int BaseAddress0, BaseAddress1; + unsigned char IRQ_Channel; BusLogic_IO_Address_T IO_Address; BusLogic_PCI_Address_T PCI_Address; unsigned short Index = 0; @@ -1065,7 +1074,7 @@ NULL, Bus, Device, IO_Address); continue; } - if (BusLogic_GlobalOptions.Bits.TraceProbe) + if (BusLogic_GlobalOptions.TraceProbe) { BusLogic_Notice("BusLogic: FlashPoint Host Adapter " "detected at\n", NULL); @@ -1077,10 +1086,10 @@ { BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; - ProbeInfo->IO_Address = IO_Address; - ProbeInfo->PCI_Address = PCI_Address; ProbeInfo->HostAdapterType = BusLogic_FlashPoint; ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus; + ProbeInfo->IO_Address = IO_Address; + ProbeInfo->PCI_Address = PCI_Address; ProbeInfo->Bus = Bus; ProbeInfo->Device = Device; ProbeInfo->IRQ_Channel = IRQ_Channel; @@ -1116,44 +1125,41 @@ FlashPoint and PCI MultiMaster Host Adapters are present, this driver will probe for FlashPoint Host Adapters first unless the BIOS primary disk is controlled by the first PCI MultiMaster Host Adapter, in which case - MultiMaster Host Adapters will be probed first. The Kernel Command Line - options "MultiMasterFirst" and "FlashPointFirst" can be used to force a - particular probe order. + MultiMaster Host Adapters will be probed first. The BusLogic Driver Options + specifications "MultiMasterFirst" and "FlashPointFirst" can be used to force + a particular probe order. */ -static void BusLogic_InitializeProbeInfoList(void) +static void BusLogic_InitializeProbeInfoList(BusLogic_HostAdapter_T + *PrototypeHostAdapter) { /* - If BusLogic_Setup has provided an I/O Address probe list, do not override - the Kernel Command Line specifications. - */ - if (BusLogic_ProbeInfoCount > 0) return; - /* If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint Host Adapters; otherwise, default to the standard ISA MultiMaster probe. */ - if (!BusLogic_ProbeOptions.Bits.NoProbePCI && pcibios_present()) + if (!BusLogic_ProbeOptions.NoProbePCI && pcibios_present()) { - if (BusLogic_ProbeOptions.Bits.ProbeMultiMasterFirst) + if (BusLogic_ProbeOptions.MultiMasterFirst) { - BusLogic_InitializeMultiMasterProbeInfo(); - BusLogic_InitializeFlashPointProbeInfo(); + BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); + BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); } - else if (BusLogic_ProbeOptions.Bits.ProbeFlashPointFirst) + else if (BusLogic_ProbeOptions.FlashPointFirst) { - BusLogic_InitializeFlashPointProbeInfo(); - BusLogic_InitializeMultiMasterProbeInfo(); + BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); + BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); } else { - int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo(); - int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo(); + int FlashPointCount = + BusLogic_InitializeFlashPointProbeInfo(PrototypeHostAdapter); + int PCIMultiMasterCount = + BusLogic_InitializeMultiMasterProbeInfo(PrototypeHostAdapter); if (FlashPointCount > 0 && PCIMultiMasterCount > 0) { BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[FlashPointCount]; - BusLogic_HostAdapter_T HostAdapterPrototype; - BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter; BusLogic_FetchHostAdapterLocalRAMRequest_T FetchHostAdapterLocalRAMRequest; BusLogic_BIOSDriveMapByte_T Drive0MapByte; @@ -1195,7 +1201,7 @@ } } } - else BusLogic_InitializeProbeInfoListISA(); + else BusLogic_InitializeProbeInfoListISA(PrototypeHostAdapter); } @@ -1211,10 +1217,13 @@ { BusLogic_AnnounceDriver(HostAdapter); if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus) - BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n" - "Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n", - HostAdapter, HostAdapter->Bus, HostAdapter->Device, - HostAdapter->IO_Address, HostAdapter->PCI_Address); + { + BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n", + HostAdapter); + BusLogic_Error("Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n", + HostAdapter, HostAdapter->Bus, HostAdapter->Device, + HostAdapter->IO_Address, HostAdapter->PCI_Address); + } else BusLogic_Error("While configuring BusLogic Host Adapter at " "I/O Address 0x%X:\n", HostAdapter, HostAdapter->IO_Address); @@ -1240,20 +1249,14 @@ */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { - FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *) - scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC); - int Retries = 10; - if (FlashPointInfo == NULL) - return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO"); - FlashPointInfo->BaseAddress = HostAdapter->IO_Address; + FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo; + FlashPointInfo->BaseAddress = + (BusLogic_Base_Address_T) HostAdapter->IO_Address; FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel; FlashPointInfo->Present = false; - while (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && - FlashPointInfo->Present) && - --Retries >= 0) ; - if (!FlashPointInfo->Present) + if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 && + FlashPointInfo->Present)) { - scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T)); BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at " "PCI Bus %d Device %d\n", HostAdapter, HostAdapter->Bus, HostAdapter->Device); @@ -1264,8 +1267,7 @@ HostAdapter); return false; } - HostAdapter->FlashPointInfo = FlashPointInfo; - if (BusLogic_GlobalOptions.Bits.TraceProbe) + if (BusLogic_GlobalOptions.TraceProbe) BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n", HostAdapter, HostAdapter->IO_Address); /* @@ -1283,7 +1285,7 @@ StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter); InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter); GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter); - if (BusLogic_GlobalOptions.Bits.TraceProbe) + if (BusLogic_GlobalOptions.TraceProbe) BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, " "Geometry 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All, @@ -1318,12 +1320,16 @@ /* - BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter, - and waits for Host Adapter Diagnostics to complete. + BusLogic_HardwareResetHostAdapter issues a Hardware Reset to the Host Adapter + and waits for Host Adapter Diagnostics to complete. If HardReset is true, a + Hard Reset is performed which also initiates a SCSI Bus Reset. Otherwise, a + Soft Reset is performed which only resets the Host Adapter without forcing a + SCSI Bus Reset. */ -static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T - *HostAdapter) +static boolean BusLogic_HardwareResetHostAdapter(BusLogic_HostAdapter_T + *HostAdapter, + boolean HardReset) { BusLogic_StatusRegister_T StatusRegister; int TimeoutCounter; @@ -1332,9 +1338,11 @@ */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { - HostAdapter->FlashPointInfo->ReportDataUnderrun = true; + FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo; + FlashPointInfo->HostSoftReset = !HardReset; + FlashPointInfo->ReportDataUnderrun = true; HostAdapter->CardHandle = - FlashPoint_HardResetHostAdapter(HostAdapter->FlashPointInfo); + FlashPoint_HardwareResetHostAdapter(FlashPointInfo); if (HostAdapter->CardHandle == FlashPoint_BadCardHandle) return false; /* Indicate the Host Adapter Hard Reset completed successfully. @@ -1342,10 +1350,12 @@ return true; } /* - Issue a Hard Reset Command to the Host Adapter. The Host Adapter should - respond by setting Diagnostic Active in the Status Register. + Issue a Hard Reset or Soft Reset Command to the Host Adapter. The Host + Adapter should respond by setting Diagnostic Active in the Status Register. */ - BusLogic_HardReset(HostAdapter); + if (HardReset) + BusLogic_HardReset(HostAdapter); + else BusLogic_SoftReset(HostAdapter); /* Wait until Diagnostic Active is set in the Status Register. */ @@ -1356,8 +1366,8 @@ if (StatusRegister.Bits.DiagnosticActive) break; udelay(100); } - if (BusLogic_GlobalOptions.Bits.TraceHardReset) - BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, " + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Active, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); if (TimeoutCounter < 0) return false; @@ -1377,8 +1387,8 @@ if (!StatusRegister.Bits.DiagnosticActive) break; udelay(100); } - if (BusLogic_GlobalOptions.Bits.TraceHardReset) - BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, " + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Diagnostic Completed, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); if (TimeoutCounter < 0) return false; @@ -1396,8 +1406,8 @@ break; udelay(100); } - if (BusLogic_GlobalOptions.Bits.TraceHardReset) - BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, " + if (BusLogic_GlobalOptions.TraceHardwareReset) + BusLogic_Notice("BusLogic_HardwareReset(0x%X): Host Adapter Ready, " "Status 0x%02X\n", HostAdapter, HostAdapter->IO_Address, StatusRegister.All); if (TimeoutCounter < 0) return false; @@ -1430,12 +1440,11 @@ /* BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic - Host Adapter. It also determines the IRQ Channel for non-PCI Host Adapters. + Host Adapter. */ static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter) { - BusLogic_Configuration_T Configuration; BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation; BusLogic_RequestedReplyLength_T RequestedReplyLength; boolean Result = true; @@ -1444,29 +1453,6 @@ */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true; /* - Issue the Inquire Configuration command if the IRQ Channel is unknown. - */ - if (HostAdapter->IRQ_Channel == 0) - if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, - NULL, 0, &Configuration, sizeof(Configuration)) - == sizeof(Configuration)) - { - if (Configuration.IRQ_Channel9) - HostAdapter->IRQ_Channel = 9; - else if (Configuration.IRQ_Channel10) - HostAdapter->IRQ_Channel = 10; - else if (Configuration.IRQ_Channel11) - HostAdapter->IRQ_Channel = 11; - else if (Configuration.IRQ_Channel12) - HostAdapter->IRQ_Channel = 12; - else if (Configuration.IRQ_Channel14) - HostAdapter->IRQ_Channel = 14; - else if (Configuration.IRQ_Channel15) - HostAdapter->IRQ_Channel = 15; - else Result = false; - } - else Result = false; - /* Issue the Inquire Extended Setup Information command. Only genuine BusLogic Host Adapters and true clones support this command. Adaptec 1542C series Host Adapters that respond to the Geometry Register I/O port will @@ -1484,7 +1470,7 @@ /* Provide tracing information if requested and return. */ - if (BusLogic_GlobalOptions.Bits.TraceProbe) + if (BusLogic_GlobalOptions.TraceProbe) BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter, HostAdapter->IO_Address, (Result ? "Found" : "Not Found")); return Result; @@ -1512,7 +1498,7 @@ BusLogic_GeometryRegister_T GeometryRegister; BusLogic_RequestedReplyLength_T RequestedReplyLength; unsigned char *TargetPointer, Character; - int i; + int TargetID, i; /* Configuration Information for FlashPoint Host Adapters is provided in the FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function. @@ -1521,7 +1507,7 @@ */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) { - FlashPoint_Info_T *FlashPointInfo = HostAdapter->FlashPointInfo; + FlashPoint_Info_T *FlashPointInfo = &HostAdapter->FlashPointInfo; TargetPointer = HostAdapter->ModelName; *TargetPointer++ = 'B'; *TargetPointer++ = 'T'; @@ -1550,8 +1536,8 @@ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit; HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8); HostAdapter->MaxLogicalUnits = 32; - HostAdapter->InitialCCBs = 64; - HostAdapter->IncrementalCCBs = 16; + HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize; + HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize; HostAdapter->DriverQueueDepth = 255; HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth; HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted; @@ -1692,18 +1678,36 @@ */ HostAdapter->SCSI_ID = Configuration.HostAdapterID; /* - Determine the Bus Type and save it in the Host Adapter structure, - and determine and save the DMA Channel for ISA Host Adapters. + Determine the Bus Type and save it in the Host Adapter structure, determine + and save the IRQ Channel if necessary, and determine and save the DMA + Channel for ISA Host Adapters. */ HostAdapter->HostAdapterBusType = BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4']; + if (HostAdapter->IRQ_Channel == 0) + { + if (Configuration.IRQ_Channel9) + HostAdapter->IRQ_Channel = 9; + else if (Configuration.IRQ_Channel10) + HostAdapter->IRQ_Channel = 10; + else if (Configuration.IRQ_Channel11) + HostAdapter->IRQ_Channel = 11; + else if (Configuration.IRQ_Channel12) + HostAdapter->IRQ_Channel = 12; + else if (Configuration.IRQ_Channel14) + HostAdapter->IRQ_Channel = 14; + else if (Configuration.IRQ_Channel15) + HostAdapter->IRQ_Channel = 15; + } if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus) - if (Configuration.DMA_Channel5) - HostAdapter->DMA_Channel = 5; - else if (Configuration.DMA_Channel6) - HostAdapter->DMA_Channel = 6; - else if (Configuration.DMA_Channel7) - HostAdapter->DMA_Channel = 7; + { + if (Configuration.DMA_Channel5) + HostAdapter->DMA_Channel = 5; + else if (Configuration.DMA_Channel6) + HostAdapter->DMA_Channel = 6; + else if (Configuration.DMA_Channel7) + HostAdapter->DMA_Channel = 7; + } /* Determine whether Extended Translation is enabled and save it in the Host Adapter structure. @@ -1837,7 +1841,7 @@ HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8); HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8); /* - Select appropriate values for the Driver Queue Depth, Mailbox Count, + Select appropriate values for the Mailbox Count, Driver Queue Depth, Initial CCBs, and Incremental CCBs variables based on whether or not Strict Round Robin Mode is supported. If Strict Round Robin Mode is supported, then there is no performance degradation in using the maximum possible @@ -1868,18 +1872,16 @@ if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0) { HostAdapter->StrictRoundRobinModeSupport = true; - HostAdapter->MailboxCount = 255; - HostAdapter->InitialCCBs = 64; - HostAdapter->IncrementalCCBs = 16; + HostAdapter->MailboxCount = BusLogic_MaxMailboxes; } else { HostAdapter->StrictRoundRobinModeSupport = false; HostAdapter->MailboxCount = 32; - HostAdapter->InitialCCBs = 32; - HostAdapter->IncrementalCCBs = 8; } HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount; + HostAdapter->InitialCCBs = 4 * BusLogic_CCB_AllocationGroupSize; + HostAdapter->IncrementalCCBs = BusLogic_CCB_AllocationGroupSize; /* Tagged Queuing support is available and operates properly on all "W" series MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with @@ -1931,57 +1933,36 @@ */ Common: /* - Initialize the Host Adapter Name and Interrupt Label fields from the - Model Name. + Initialize the Host Adapter Full Model Name from the Model Name. */ strcpy(HostAdapter->FullModelName, "BusLogic "); strcat(HostAdapter->FullModelName, HostAdapter->ModelName); - strcpy(HostAdapter->InterruptLabel, HostAdapter->FullModelName); /* Select an appropriate value for the Tagged Queue Depth either from a - Command Line Entry, or based on whether this Host Adapter requires that ISA - Bounce Buffers be used. The Tagged Queue Depth is left at 0 for automatic - determination in BusLogic_SelectQueueDepths. Initialize the Untagged Queue - Depth. Tagged Queuing is disabled by default when the Tagged Queue Depth - is 1 since queuing multiple commands is not possible. - */ - if (HostAdapter->CommandLineEntry != NULL && - HostAdapter->CommandLineEntry->TaggedQueueDepth > 0) - HostAdapter->TaggedQueueDepth = - HostAdapter->CommandLineEntry->TaggedQueueDepth; - else if (HostAdapter->BounceBuffersRequired) - HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepthBounceBuffers; - else HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepthAutomatic; - HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth; - if (HostAdapter->UntaggedQueueDepth > HostAdapter->TaggedQueueDepth && - HostAdapter->TaggedQueueDepth > 0) - HostAdapter->UntaggedQueueDepth = HostAdapter->TaggedQueueDepth; - if (HostAdapter->TaggedQueueDepth == 1) - HostAdapter->TaggedQueuingPermitted = 0; - /* - Select an appropriate value for Bus Settle Time either from a Command - Line Entry, or from BusLogic_DefaultBusSettleTime. - */ - if (HostAdapter->CommandLineEntry != NULL && - HostAdapter->CommandLineEntry->BusSettleTime > 0) - HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime; - else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime; - /* - Select an appropriate value for Local Options from a Command Line Entry. - */ - if (HostAdapter->CommandLineEntry != NULL) - HostAdapter->LocalOptions = HostAdapter->CommandLineEntry->LocalOptions; - /* - Select appropriate values for the Error Recovery Strategy array either from - a Command Line Entry, or using BusLogic_ErrorRecovery_Default. - */ - if (HostAdapter->CommandLineEntry != NULL) - memcpy(HostAdapter->ErrorRecoveryStrategy, - HostAdapter->CommandLineEntry->ErrorRecoveryStrategy, - sizeof(HostAdapter->ErrorRecoveryStrategy)); - else memset(HostAdapter->ErrorRecoveryStrategy, - BusLogic_ErrorRecovery_Default, - sizeof(HostAdapter->ErrorRecoveryStrategy)); + BusLogic Driver Options specification, or based on whether this Host + Adapter requires that ISA Bounce Buffers be used. The Tagged Queue Depth + is left at 0 for automatic determination in BusLogic_SelectQueueDepths. + Initialize the Untagged Queue Depth. + */ + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + { + unsigned char QueueDepth = 0; + if (HostAdapter->DriverOptions != NULL && + HostAdapter->DriverOptions->QueueDepth[TargetID] > 0) + QueueDepth = HostAdapter->DriverOptions->QueueDepth[TargetID]; + else if (HostAdapter->BounceBuffersRequired) + QueueDepth = BusLogic_TaggedQueueDepthBB; + HostAdapter->QueueDepth[TargetID] = QueueDepth; + } + if (HostAdapter->BounceBuffersRequired) + HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepthBB; + else HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth; + if (HostAdapter->DriverOptions != NULL) + HostAdapter->CommonQueueDepth = + HostAdapter->DriverOptions->CommonQueueDepth; + if (HostAdapter->CommonQueueDepth > 0 && + HostAdapter->CommonQueueDepth < HostAdapter->UntaggedQueueDepth) + HostAdapter->UntaggedQueueDepth = HostAdapter->CommonQueueDepth; /* Tagged Queuing is only allowed if Disconnect/Reconnect is permitted. Therefore, mask the Tagged Queuing Permitted Default bits with the @@ -1989,15 +1970,34 @@ */ HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted; /* - Combine the default Tagged Queuing Permitted bits with any Command - Line Entry Tagged Queuing specification. + Combine the default Tagged Queuing Permitted bits with any BusLogic Driver + Options Tagged Queuing specification. */ - if (HostAdapter->CommandLineEntry != NULL) + if (HostAdapter->DriverOptions != NULL) HostAdapter->TaggedQueuingPermitted = - (HostAdapter->CommandLineEntry->TaggedQueuingPermitted & - HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) | + (HostAdapter->DriverOptions->TaggedQueuingPermitted & + HostAdapter->DriverOptions->TaggedQueuingPermittedMask) | (HostAdapter->TaggedQueuingPermitted & - ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask); + ~HostAdapter->DriverOptions->TaggedQueuingPermittedMask); + /* + Select appropriate values for the Error Recovery Strategy array + either from a BusLogic Driver Options specification, or using + BusLogic_ErrorRecovery_Default. + */ + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + if (HostAdapter->DriverOptions != NULL) + HostAdapter->ErrorRecoveryStrategy[TargetID] = + HostAdapter->DriverOptions->ErrorRecoveryStrategy[TargetID]; + else HostAdapter->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_Default; + /* + Select an appropriate value for Bus Settle Time either from a BusLogic + Driver Options specification, or from BusLogic_DefaultBusSettleTime. + */ + if (HostAdapter->DriverOptions != NULL && + HostAdapter->DriverOptions->BusSettleTime > 0) + HostAdapter->BusSettleTime = HostAdapter->DriverOptions->BusSettleTime; + else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime; /* Indicate reading the Host Adapter Configuration completed successfully. */ @@ -2017,7 +2017,8 @@ unsigned short SynchronousPermitted, FastPermitted; unsigned short UltraPermitted, WidePermitted; unsigned short DisconnectPermitted, TaggedQueuingPermitted; - boolean CommonSynchronousNegotiation, CommonErrorRecovery; + boolean CommonSynchronousNegotiation, CommonTaggedQueueDepth; + boolean CommonErrorRecovery; char SynchronousString[BusLogic_MaxTargetDevices+1]; char WideString[BusLogic_MaxTargetDevices+1]; char DisconnectString[BusLogic_MaxTargetDevices+1]; @@ -2083,22 +2084,26 @@ CommonSynchronousNegotiation = true; } else if (SynchronousPermitted == AllTargetsMask) - if (FastPermitted == 0) - { - SynchronousMessage = "Slow"; - CommonSynchronousNegotiation = true; - } - else if (FastPermitted == AllTargetsMask) - if (UltraPermitted == 0) + { + if (FastPermitted == 0) { - SynchronousMessage = "Fast"; + SynchronousMessage = "Slow"; CommonSynchronousNegotiation = true; } - else if (UltraPermitted == AllTargetsMask) + else if (FastPermitted == AllTargetsMask) { - SynchronousMessage = "Ultra"; - CommonSynchronousNegotiation = true; + if (UltraPermitted == 0) + { + SynchronousMessage = "Fast"; + CommonSynchronousNegotiation = true; + } + else if (UltraPermitted == AllTargetsMask) + { + SynchronousMessage = "Ultra"; + CommonSynchronousNegotiation = true; + } } + } if (!CommonSynchronousNegotiation) { for (TargetID = 0; @@ -2175,9 +2180,20 @@ HostAdapter, HostAdapter->DriverQueueDepth, HostAdapter->DriverScatterGatherLimit); BusLogic_Info(" Tagged Queue Depth: ", HostAdapter); - if (HostAdapter->TaggedQueueDepth > 0) - BusLogic_Info("%d", HostAdapter, HostAdapter->TaggedQueueDepth); - else BusLogic_Info("Automatic", HostAdapter); + CommonTaggedQueueDepth = true; + for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->QueueDepth[TargetID] != HostAdapter->QueueDepth[0]) + { + CommonTaggedQueueDepth = false; + break; + } + if (CommonTaggedQueueDepth) + { + if (HostAdapter->QueueDepth[0] > 0) + BusLogic_Info("%d", HostAdapter, HostAdapter->QueueDepth[0]); + else BusLogic_Info("Automatic", HostAdapter); + } + else BusLogic_Info("Individual", HostAdapter); BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter, HostAdapter->UntaggedQueueDepth); CommonErrorRecovery = true; @@ -2238,8 +2254,6 @@ static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter) { - BusLogic_HostAdapter_T *FirstHostAdapter = - BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel]; if (HostAdapter->IRQ_Channel == 0) { BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n", @@ -2247,24 +2261,15 @@ return false; } /* - Acquire exclusive or shared access to the IRQ Channel if necessary. + Acquire shared access to the IRQ Channel. */ - if (FirstHostAdapter->Next == NULL) - { - if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, - SA_INTERRUPT | SA_SHIRQ, - HostAdapter->InterruptLabel, NULL) < 0) - { - BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", - HostAdapter, HostAdapter->IRQ_Channel); - return false; - } - } - else if (strlen(FirstHostAdapter->InterruptLabel) + 11 - < sizeof(FirstHostAdapter->InterruptLabel)) + if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler, + SA_INTERRUPT | SA_SHIRQ, + HostAdapter->FullModelName, HostAdapter) < 0) { - strcat(FirstHostAdapter->InterruptLabel, " + "); - strcat(FirstHostAdapter->InterruptLabel, HostAdapter->ModelName); + BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n", + HostAdapter, HostAdapter->IRQ_Channel); + return false; } HostAdapter->IRQ_ChannelAcquired = true; /* @@ -2297,14 +2302,11 @@ static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter) { - BusLogic_HostAdapter_T *FirstHostAdapter = - BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel]; /* - Release exclusive or shared access to the IRQ Channel. + Release shared access to the IRQ Channel. */ if (HostAdapter->IRQ_ChannelAcquired) - if (FirstHostAdapter->Next == NULL) - free_irq(HostAdapter->IRQ_Channel, NULL); + free_irq(HostAdapter->IRQ_Channel, HostAdapter); /* Release exclusive access to the DMA Channel. */ @@ -2314,50 +2316,6 @@ /* - BusLogic_TestInterrupts tests for proper functioning of the Host Adapter - Interrupt Register and that interrupts generated by the Host Adapter are - getting through to the Interrupt Handler. A large proportion of initial - problems with installing PCI Host Adapters are due to configuration problems - where either the Host Adapter or Motherboard is configured incorrectly, and - interrupts do not get through as a result. -*/ - -static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter) -{ - unsigned int InitialInterruptCount, FinalInterruptCount; - int TestCount = 5, i; - /* - FlashPoint Host Adapters do not provide for an interrupt test. - */ - if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true; - /* - Inhibit the Interrupt Test if requested. - */ - if (HostAdapter->LocalOptions.Bits.InhibitInterruptTest) return true; - /* - Issue the Test Command Complete Interrupt commands. - */ - InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; - for (i = 0; i < TestCount; i++) - BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt, - NULL, 0, NULL, 0); - FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel]; - /* - Verify that BusLogic_InterruptHandler was called at least TestCount - times. Shared IRQ Channels could cause more than TestCount interrupts to - occur, but there should never be fewer than TestCount, unless one or more - interrupts were lost. - */ - if (FinalInterruptCount < InitialInterruptCount + TestCount) - return BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST"); - /* - Indicate the Host Adapter Interrupt Test completed successfully. - */ - return true; -} - - -/* BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only function called during SCSI Host Adapter detection which modifies the state of the Host Adapter from its initial power on or hard reset state. @@ -2371,25 +2329,42 @@ BusLogic_SetCCBFormatRequest_T SetCCBFormatRequest; int TargetID; /* + Initialize the pointers to the first and last CCBs that are queued for + completion processing. + */ + HostAdapter->FirstCompletedCCB = NULL; + HostAdapter->LastCompletedCCB = NULL; + /* Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active, Command Successful Flag, Active Commands, and Commands Since Reset for each Target Device. */ for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; - memset(HostAdapter->TaggedQueuingActive, false, - sizeof(HostAdapter->TaggedQueuingActive)); - memset(HostAdapter->CommandSuccessfulFlag, false, - sizeof(HostAdapter->CommandSuccessfulFlag)); - memset(HostAdapter->ActiveCommands, 0, - sizeof(HostAdapter->ActiveCommands)); - memset(HostAdapter->CommandsSinceReset, 0, - sizeof(HostAdapter->CommandsSinceReset)); + { + HostAdapter->BusDeviceResetPendingCCB[TargetID] = NULL; + HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false; + HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false; + HostAdapter->ActiveCommands[TargetID] = 0; + HostAdapter->CommandsSinceReset[TargetID] = 0; + } /* FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes. */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) goto Done; /* + Initialize the Outgoing and Incoming Mailbox pointers. + */ + HostAdapter->FirstOutgoingMailbox = + (BusLogic_OutgoingMailbox_T *) HostAdapter->MailboxSpace; + HostAdapter->LastOutgoingMailbox = + HostAdapter->FirstOutgoingMailbox + HostAdapter->MailboxCount - 1; + HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; + HostAdapter->FirstIncomingMailbox = + (BusLogic_IncomingMailbox_T *) (HostAdapter->LastOutgoingMailbox + 1); + HostAdapter->LastIncomingMailbox = + HostAdapter->FirstIncomingMailbox + HostAdapter->MailboxCount - 1; + HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; + /* Initialize the Outgoing and Incoming Mailbox structures. */ memset(HostAdapter->FirstOutgoingMailbox, 0, @@ -2397,11 +2372,6 @@ memset(HostAdapter->FirstIncomingMailbox, 0, HostAdapter->MailboxCount * sizeof(BusLogic_IncomingMailbox_T)); /* - Initialize the pointers to the Next Mailboxes. - */ - HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox; - HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; - /* Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes. */ ExtendedMailboxRequest.MailboxCount = HostAdapter->MailboxCount; @@ -2442,11 +2412,14 @@ Announce Successful Initialization. */ Done: - if (HostAdapter->HostAdapterInitialized) - BusLogic_Warning("*** %s Initialized Successfully ***\n", - HostAdapter, HostAdapter->FullModelName); - else BusLogic_Info("*** %s Initialized Successfully ***\n", - HostAdapter, HostAdapter->FullModelName); + if (!HostAdapter->HostAdapterInitialized) + { + BusLogic_Info("*** %s Initialized Successfully ***\n", + HostAdapter, HostAdapter->FullModelName); + BusLogic_Info("\n", HostAdapter); + } + else BusLogic_Warning("*** %s Initialized Successfully ***\n", + HostAdapter, HostAdapter->FullModelName); HostAdapter->HostAdapterInitialized = true; /* Indicate the Host Adapter Initialization completed successfully. @@ -2457,7 +2430,7 @@ /* BusLogic_TargetDeviceInquiry inquires about the Target Devices accessible - through Host Adapter and reports on the results. + through Host Adapter. */ static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T @@ -2468,7 +2441,7 @@ BusLogic_SetupInformation_T SetupInformation; BusLogic_SynchronousPeriod_T SynchronousPeriod; BusLogic_RequestedReplyLength_T RequestedReplyLength; - int TargetDevicesFound = 0, TargetID; + int TargetID; /* Wait a few seconds between the Host Adapter Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI Commands. Some SCSI devices get @@ -2480,13 +2453,11 @@ */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true; /* - Inhibit the Target Devices Inquiry if requested. + Inhibit the Target Device Inquiry if requested. */ - if (HostAdapter->LocalOptions.Bits.InhibitTargetInquiry) - { - BusLogic_Info(" Target Device Inquiry Inhibited\n", HostAdapter); - return true; - } + if (HostAdapter->DriverOptions != NULL && + HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry) + return true; /* Issue the Inquire Target Devices command for host adapters with firmware version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command @@ -2502,6 +2473,9 @@ &InstalledDevices, sizeof(InstalledDevices)) != sizeof(InstalledDevices)) return BusLogic_Failure(HostAdapter, "INQUIRE TARGET DEVICES"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->TargetFlags[TargetID].TargetExists = + (InstalledDevices & (1 << TargetID) ? true : false); } else { @@ -2511,10 +2485,9 @@ != sizeof(InstalledDevicesID0to7)) return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7"); - InstalledDevices = 0; for (TargetID = 0; TargetID < 8; TargetID++) - if (InstalledDevicesID0to7[TargetID] != 0) - InstalledDevices |= (1 << TargetID); + HostAdapter->TargetFlags[TargetID].TargetExists = + (InstalledDevicesID0to7[TargetID] != 0 ? true : false); } /* Issue the Inquire Setup Information command. @@ -2525,6 +2498,19 @@ &SetupInformation, sizeof(SetupInformation)) != sizeof(SetupInformation)) return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->SynchronousOffset[TargetID] = + (TargetID < 8 + ? SetupInformation.SynchronousValuesID0to7[TargetID].Offset + : SetupInformation.SynchronousValuesID8to15[TargetID-8].Offset); + if (strcmp(HostAdapter->FirmwareVersion, "5.06L") >= 0) + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->TargetFlags[TargetID].WideTransfersActive = + (TargetID < 8 + ? (SetupInformation.WideTransfersActiveID0to7 & (1 << TargetID) + ? true : false) + : (SetupInformation.WideTransfersActiveID8to15 & (1 << (TargetID-8)) + ? true : false)); /* Issue the Inquire Synchronous Period command. */ @@ -2536,65 +2522,15 @@ &SynchronousPeriod, sizeof(SynchronousPeriod)) != sizeof(SynchronousPeriod)) return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD"); + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + HostAdapter->SynchronousPeriod[TargetID] = SynchronousPeriod[TargetID]; } else for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0) - SynchronousPeriod[TargetID] = + HostAdapter->SynchronousPeriod[TargetID] = 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID] .TransferPeriod; - else SynchronousPeriod[TargetID] = 0; - /* - Save the Installed Devices, Synchronous Values, and Synchronous Period - information in the Host Adapter structure. - */ - HostAdapter->InstalledDevices = InstalledDevices; - memcpy(HostAdapter->SynchronousValues, - SetupInformation.SynchronousValuesID0to7, - sizeof(BusLogic_SynchronousValues8_T)); - if (HostAdapter->HostWideSCSI) - memcpy(&HostAdapter->SynchronousValues[8], - SetupInformation.SynchronousValuesID8to15, - sizeof(BusLogic_SynchronousValues8_T)); - memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod, - sizeof(BusLogic_SynchronousPeriod_T)); - /* - Report on the Target Devices found. - */ - for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (HostAdapter->InstalledDevices & (1 << TargetID)) - { - int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID]; - if (SynchronousPeriod > 10) - { - int SynchronousTransferRate = 100000000 / SynchronousPeriod; - int RoundedSynchronousTransferRate = - (SynchronousTransferRate + 5000) / 10000; - BusLogic_Info(" Target %d: Synchronous at " - "%d.%02d mega-transfers/second, offset %d\n", - HostAdapter, TargetID, - RoundedSynchronousTransferRate / 100, - RoundedSynchronousTransferRate % 100, - HostAdapter->SynchronousValues[TargetID].Offset); - } - else if (SynchronousPeriod > 0) - { - int SynchronousTransferRate = 100000000 / SynchronousPeriod; - int RoundedSynchronousTransferRate = - (SynchronousTransferRate + 50000) / 100000; - BusLogic_Info(" Target %d: Synchronous at " - "%d.%01d mega-transfers/second, offset %d\n", - HostAdapter, TargetID, - RoundedSynchronousTransferRate / 10, - RoundedSynchronousTransferRate % 10, - HostAdapter->SynchronousValues[TargetID].Offset); - } - else BusLogic_Info(" Target %d: Asynchronous\n", - HostAdapter, TargetID); - TargetDevicesFound++; - } - if (TargetDevicesFound == 0) - BusLogic_Info(" No Target Devices Found\n", HostAdapter); /* Indicate the Target Device Inquiry completed successfully. */ @@ -2603,10 +2539,87 @@ /* - BusLogic_InitializeHostStructure initializes the fields in the SCSI Host - structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the - SCSI Host structure are intentionally left uninitialized, as this driver - handles acquisition and release of these resources explicitly, as well as + BusLogic_ReportTargetDeviceInfo reports about the Target Devices accessible + through Host Adapter. +*/ + +static void BusLogic_ReportTargetDeviceInfo(BusLogic_HostAdapter_T + *HostAdapter) +{ + int TargetID; + /* + Inhibit the Target Device Inquiry and Reporting if requested. + */ + if (BusLogic_MultiMasterHostAdapterP(HostAdapter) && + HostAdapter->DriverOptions != NULL && + HostAdapter->DriverOptions->LocalOptions.InhibitTargetInquiry) + return; + /* + Report on the Target Devices found. + */ + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (TargetFlags->TargetExists && !TargetFlags->TargetInfoReported) + { + int SynchronousTransferRate = 0; + if (BusLogic_FlashPointHostAdapterP(HostAdapter)) + { + boolean WideTransfersActive; + FlashPoint_InquireTargetInfo( + HostAdapter->CardHandle, TargetID, + &HostAdapter->SynchronousPeriod[TargetID], + &HostAdapter->SynchronousOffset[TargetID], + &WideTransfersActive); + TargetFlags->WideTransfersActive = WideTransfersActive; + } + else if (TargetFlags->WideTransfersSupported && + (HostAdapter->WidePermitted & (1 << TargetID)) && + strcmp(HostAdapter->FirmwareVersion, "5.06L") < 0) + TargetFlags->WideTransfersActive = true; + if (HostAdapter->SynchronousPeriod[TargetID] > 0) + SynchronousTransferRate = + 100000 / HostAdapter->SynchronousPeriod[TargetID]; + if (TargetFlags->WideTransfersActive) + SynchronousTransferRate <<= 1; + if (SynchronousTransferRate >= 9950) + { + SynchronousTransferRate = (SynchronousTransferRate + 50) / 100; + BusLogic_Info("Target %d: Queue Depth %d, %sSynchronous at " + "%d.%01d MB/sec, offset %d\n", + HostAdapter, TargetID, + HostAdapter->QueueDepth[TargetID], + (TargetFlags->WideTransfersActive ? "Wide " : ""), + SynchronousTransferRate / 10, + SynchronousTransferRate % 10, + HostAdapter->SynchronousOffset[TargetID]); + } + else if (SynchronousTransferRate > 0) + { + SynchronousTransferRate = (SynchronousTransferRate + 5) / 10; + BusLogic_Info("Target %d: Queue Depth %d, %sSynchronous at " + "%d.%02d MB/sec, offset %d\n", + HostAdapter, TargetID, + HostAdapter->QueueDepth[TargetID], + (TargetFlags->WideTransfersActive ? "Wide " : ""), + SynchronousTransferRate / 100, + SynchronousTransferRate % 100, + HostAdapter->SynchronousOffset[TargetID]); + } + else BusLogic_Info("Target %d: Queue Depth %d, Asynchronous\n", + HostAdapter, TargetID, + HostAdapter->QueueDepth[TargetID]); + TargetFlags->TargetInfoReported = true; + } + } +} + + +/* + BusLogic_InitializeHostStructure initializes the fields in the SCSI Host + structure. The base, io_port, n_io_ports, irq, and dma_channel fields in the + SCSI Host structure are intentionally left uninitialized, as this driver + handles acquisition and release of these resources explicitly, as well as ensuring exclusive access to the Host Adapter hardware and data structures through explicit acquisition and release of the Host Adapter's Lock. */ @@ -2628,9 +2641,11 @@ /* - BusLogic_SelectQueueDepths selects Queue Depths for each Target Device - based on the Host Adapter's Total Queue Depth and the number, type, speed, - and capabilities of the Target Devices. + BusLogic_SelectQueueDepths selects Queue Depths for each Target Device based + on the Host Adapter's Total Queue Depth and the number, type, speed, and + capabilities of the Target Devices. When called for the last Host Adapter, + it reports on the Target Device Information for all BusLogic Host Adapters + since all the Target Devices have now been probed. */ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host, @@ -2638,41 +2653,69 @@ { BusLogic_HostAdapter_T *HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata; - int TaggedQueueDepth = HostAdapter->TaggedQueueDepth; - int UntaggedQueueDepth = HostAdapter->UntaggedQueueDepth; - int TaggedDeviceCount = 0, UntaggedDeviceCount = 0; - int DesiredCCBs = HostAdapter->MaxTargetDevices - 1; + int TaggedDeviceCount = 0, AutomaticTaggedDeviceCount = 0; + int UntaggedDeviceCount = 0, AutomaticTaggedQueueDepth = 0; + int AllocatedQueueDepth = 0; SCSI_Device_T *Device; - for (Device = DeviceList; Device != NULL; Device = Device->next) - if (Device->host == Host) + int TargetID; + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->TargetFlags[TargetID].TargetExists) { - if (Device->tagged_supported && - (HostAdapter->TaggedQueuingPermitted & (1 << Device->id))) - TaggedDeviceCount++; - else UntaggedDeviceCount++; + int QueueDepth = HostAdapter->QueueDepth[TargetID]; + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingSupported && + (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) + { + TaggedDeviceCount++; + if (QueueDepth == 0) AutomaticTaggedDeviceCount++; + } + else + { + UntaggedDeviceCount++; + if (QueueDepth == 0 || + QueueDepth > HostAdapter->UntaggedQueueDepth) + { + QueueDepth = HostAdapter->UntaggedQueueDepth; + HostAdapter->QueueDepth[TargetID] = QueueDepth; + } + } + AllocatedQueueDepth += QueueDepth; + if (QueueDepth == 1) + HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID); } - if (TaggedQueueDepth == 0 && TaggedDeviceCount > 0) + HostAdapter->TargetDeviceCount = TaggedDeviceCount + UntaggedDeviceCount; + if (AutomaticTaggedDeviceCount > 0) { - TaggedQueueDepth = - 1 + ((HostAdapter->HostAdapterQueueDepth - - UntaggedDeviceCount * UntaggedQueueDepth) - / TaggedDeviceCount); - if (TaggedQueueDepth > BusLogic_PreferredTaggedQueueDepth) - TaggedQueueDepth = BusLogic_PreferredTaggedQueueDepth; + AutomaticTaggedQueueDepth = + (HostAdapter->HostAdapterQueueDepth - AllocatedQueueDepth) + / AutomaticTaggedDeviceCount; + if (AutomaticTaggedQueueDepth > BusLogic_MaxAutomaticTaggedQueueDepth) + AutomaticTaggedQueueDepth = BusLogic_MaxAutomaticTaggedQueueDepth; + if (AutomaticTaggedQueueDepth < BusLogic_MinAutomaticTaggedQueueDepth) + AutomaticTaggedQueueDepth = BusLogic_MinAutomaticTaggedQueueDepth; + for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) + if (HostAdapter->TargetFlags[TargetID].TargetExists && + HostAdapter->QueueDepth[TargetID] == 0) + { + AllocatedQueueDepth += AutomaticTaggedQueueDepth; + HostAdapter->QueueDepth[TargetID] = AutomaticTaggedQueueDepth; + } } for (Device = DeviceList; Device != NULL; Device = Device->next) if (Device->host == Host) - { - if (Device->tagged_supported && - (HostAdapter->TaggedQueuingPermitted & (1 << Device->id))) - Device->queue_depth = TaggedQueueDepth; - else Device->queue_depth = UntaggedQueueDepth; - HostAdapter->QueueDepth[Device->id] = Device->queue_depth; - DesiredCCBs += Device->queue_depth; - } + Device->queue_depth = HostAdapter->QueueDepth[Device->id]; + /* Allocate an extra CCB for each Target Device for a Bus Device Reset. */ + AllocatedQueueDepth += HostAdapter->TargetDeviceCount; + if (AllocatedQueueDepth > HostAdapter->DriverQueueDepth) + AllocatedQueueDepth = HostAdapter->DriverQueueDepth; BusLogic_CreateAdditionalCCBs(HostAdapter, - DesiredCCBs - HostAdapter->AllocatedCCBs, + AllocatedQueueDepth + - HostAdapter->AllocatedCCBs, false); + if (HostAdapter == BusLogic_LastRegisteredHostAdapter) + for (HostAdapter = BusLogic_FirstRegisteredHostAdapter; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + BusLogic_ReportTargetDeviceInfo(HostAdapter); } @@ -2686,50 +2729,48 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate) { - int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0, ProbeIndex; - char *MessageBuffer = NULL; - if (BusLogic_ProbeOptions.Bits.NoProbe) return 0; - BusLogic_InitializeProbeInfoList(); + int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex; + BusLogic_HostAdapter_T *PrototypeHostAdapter; + if (BusLogic_ProbeOptions.NoProbe) return 0; + BusLogic_ProbeInfoList = (BusLogic_ProbeInfo_T *) + kmalloc(BusLogic_MaxHostAdapters * sizeof(BusLogic_ProbeInfo_T), + GFP_ATOMIC); + if (BusLogic_ProbeInfoList == NULL) + { + BusLogic_Error("BusLogic: Unable to allocate Probe Info List\n", NULL); + return 0; + } + memset(BusLogic_ProbeInfoList, 0, + BusLogic_MaxHostAdapters * sizeof(BusLogic_ProbeInfo_T)); + PrototypeHostAdapter = (BusLogic_HostAdapter_T *) + kmalloc(sizeof(BusLogic_HostAdapter_T), GFP_ATOMIC); + if (PrototypeHostAdapter == NULL) + { + kfree(BusLogic_ProbeInfoList); + BusLogic_Error("BusLogic: Unable to allocate Prototype " + "Host Adapter\n", NULL); + return 0; + } + memset(PrototypeHostAdapter, 0, sizeof(BusLogic_HostAdapter_T)); + if (BusLogic_Options != NULL) + BusLogic_ParseDriverOptions(BusLogic_Options); + BusLogic_InitializeProbeInfoList(PrototypeHostAdapter); for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++) { BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex]; - BusLogic_HostAdapter_T HostAdapterPrototype; - BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype; + BusLogic_HostAdapter_T *HostAdapter = PrototypeHostAdapter; SCSI_Host_T *Host; if (ProbeInfo->IO_Address == 0) continue; memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T)); - HostAdapter->IO_Address = ProbeInfo->IO_Address; - HostAdapter->PCI_Address = ProbeInfo->PCI_Address; HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType; HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType; + HostAdapter->IO_Address = ProbeInfo->IO_Address; + HostAdapter->PCI_Address = ProbeInfo->PCI_Address; HostAdapter->Bus = ProbeInfo->Bus; HostAdapter->Device = ProbeInfo->Device; HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel; HostAdapter->AddressCount = - BusLogic_HostAdapter_AddressCount[HostAdapter->HostAdapterType]; - if (MessageBuffer == NULL) - MessageBuffer = - scsi_init_malloc(BusLogic_MessageBufferSize, GFP_ATOMIC); - if (MessageBuffer == NULL) - { - BusLogic_Error("BusLogic: Unable to allocate Message Buffer\n", - HostAdapter); - return BusLogicHostAdapterCount; - } - HostAdapter->MessageBuffer = MessageBuffer; - /* - If an explicit I/O Address was specified, Initialize the Command Line - Entry field and inhibit the check for whether the I/O Address range is - already in use. - */ - if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && - BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == - HostAdapter->IO_Address) - HostAdapter->CommandLineEntry = - &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; - else if (check_region(HostAdapter->IO_Address, - HostAdapter->AddressCount) < 0) - continue; + BusLogic_HostAdapterAddressCount[HostAdapter->HostAdapterType]; /* Probe the Host Adapter. If unsuccessful, abort further initialization. */ @@ -2738,22 +2779,20 @@ Hard Reset the Host Adapter. If unsuccessful, abort further initialization. */ - if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue; + if (!BusLogic_HardwareResetHostAdapter(HostAdapter, true)) continue; /* Check the Host Adapter. If unsuccessful, abort further initialization. */ if (!BusLogic_CheckHostAdapter(HostAdapter)) continue; /* - Initialize the Command Line Entry field if an explicit I/O Address - was not specified. + Initialize the Driver Options field if provided. */ - if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount && - BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0) - HostAdapter->CommandLineEntry = - &BusLogic_CommandLineEntries[CommandLineEntryIndex++]; + if (DriverOptionsIndex < BusLogic_DriverOptionsCount) + HostAdapter->DriverOptions = + &BusLogic_DriverOptions[DriverOptionsIndex++]; /* Announce the Driver Version and Date, Author's Name, Copyright Notice, - and Contact Address. + and Electronic Mail Address. */ BusLogic_AnnounceDriver(HostAdapter); /* @@ -2771,32 +2810,25 @@ */ Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T)); HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata; - memcpy(HostAdapter, &HostAdapterPrototype, - sizeof(BusLogic_HostAdapter_T)); + memcpy(HostAdapter, PrototypeHostAdapter, sizeof(BusLogic_HostAdapter_T)); HostAdapter->SCSI_Host = Host; HostAdapter->HostNumber = Host->host_no; Host->select_queue_depths = BusLogic_SelectQueueDepths; /* Add Host Adapter to the end of the list of registered BusLogic - Host Adapters. In order for Command Complete Interrupts to be - properly dismissed by BusLogic_InterruptHandler, the Host Adapter - must be registered. + Host Adapters. */ BusLogic_RegisterHostAdapter(HostAdapter); /* Read the Host Adapter Configuration, Configure the Host Adapter, - Acquire the System Resources necessary to use the Host Adapter, - then Test Interrupts, Create the Mailboxes, Initial CCBs, and - Target Device Statistics, Initialize the Host Adapter, and - finally perform Target Device Inquiry. + Acquire the System Resources necessary to use the Host Adapter, then + Create the Initial CCBs, Initialize the Host Adapter, and finally + perform Target Device Inquiry. */ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) && BusLogic_ReportHostAdapterConfiguration(HostAdapter) && BusLogic_AcquireResources(HostAdapter) && - BusLogic_TestInterrupts(HostAdapter) && - BusLogic_CreateMailboxes(HostAdapter) && BusLogic_CreateInitialCCBs(HostAdapter) && - BusLogic_CreateTargetDeviceStatistics(HostAdapter) && BusLogic_InitializeHostAdapter(HostAdapter) && BusLogic_TargetDeviceInquiry(HostAdapter)) { @@ -2806,7 +2838,6 @@ Name of the Host Adapter will appear, and initialize the SCSI Host structure. */ - MessageBuffer = NULL; release_region(HostAdapter->IO_Address, HostAdapter->AddressCount); request_region(HostAdapter->IO_Address, @@ -2818,24 +2849,22 @@ else { /* - An error occurred during Host Adapter Configuration Querying, - Host Adapter Configuration, Resource Acquisition, Interrupt - Testing, CCB Creation, Host Adapter Initialization, or Target - Device Inquiry, so remove Host Adapter from the list of - registered BusLogic Host Adapters, destroy the Target Device - Statistics, CCBs, and Mailboxes, Release the System Resources, - and Unregister the SCSI Host. + An error occurred during Host Adapter Configuration Querying, Host + Adapter Configuration, Resource Acquisition, CCB Creation, Host + Adapter Initialization, or Target Device Inquiry, so remove Host + Adapter from the list of registered BusLogic Host Adapters, destroy + the CCBs, Release the System Resources, and Unregister the SCSI + Host. */ - BusLogic_DestroyTargetDeviceStatistics(HostAdapter); BusLogic_DestroyCCBs(HostAdapter); - BusLogic_DestroyMailboxes(HostAdapter); BusLogic_ReleaseResources(HostAdapter); BusLogic_UnregisterHostAdapter(HostAdapter); scsi_unregister(Host); } } - if (MessageBuffer != NULL) - scsi_init_free(MessageBuffer, BusLogic_MessageBufferSize); + kfree(PrototypeHostAdapter); + kfree(BusLogic_ProbeInfoList); + BusLogic_ProbeInfoList = NULL; return BusLogicHostAdapterCount; } @@ -2851,21 +2880,16 @@ BusLogic_HostAdapter_T *HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata; /* - FlashPoint Host Adapters must also be released by the FlashPoint + FlashPoint Host Adapters must first be released by the FlashPoint SCCB Manager. */ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) - { - FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle); - scsi_init_free((char *) HostAdapter->FlashPointInfo, - sizeof(FlashPoint_Info_T)); - } + FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle); /* - Destroy the CCBs and Mailboxes, and release any system resources acquired - to support Host Adapter. + Destroy the CCBs and release any system resources acquired to + support Host Adapter. */ BusLogic_DestroyCCBs(HostAdapter); - BusLogic_DestroyMailboxes(HostAdapter); BusLogic_ReleaseResources(HostAdapter); /* Release usage of the I/O Address range. @@ -2885,19 +2909,20 @@ static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB) { + BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter; CCB->Status = BusLogic_CCB_Completed; CCB->Next = NULL; - if (BusLogic_FirstCompletedCCB == NULL) + if (HostAdapter->FirstCompletedCCB == NULL) { - BusLogic_FirstCompletedCCB = CCB; - BusLogic_LastCompletedCCB = CCB; + HostAdapter->FirstCompletedCCB = CCB; + HostAdapter->LastCompletedCCB = CCB; } else { - BusLogic_LastCompletedCCB->Next = CCB; - BusLogic_LastCompletedCCB = CCB; + HostAdapter->LastCompletedCCB->Next = CCB; + HostAdapter->LastCompletedCCB = CCB; } - CCB->HostAdapter->ActiveCommands[CCB->TargetID]--; + HostAdapter->ActiveCommands[CCB->TargetID]--; } @@ -2988,27 +3013,29 @@ BusLogic_CCB_T *CCB = (BusLogic_CCB_T *) Bus_to_Virtual(NextIncomingMailbox->CCB); if (CompletionCode != BusLogic_AbortedCommandNotFound) - if (CCB->Status == BusLogic_CCB_Active || - CCB->Status == BusLogic_CCB_Reset) - { - /* - Save the Completion Code for this CCB and queue the CCB - for completion processing. - */ - CCB->CompletionCode = CompletionCode; - BusLogic_QueueCompletedCCB(CCB); - } - else - { - /* - If a CCB ever appears in an Incoming Mailbox and is not marked as - status Active or Reset, then there is most likely a bug in the - Host Adapter firmware. - */ - BusLogic_Warning("Illegal CCB #%ld status %d in " - "Incoming Mailbox\n", HostAdapter, - CCB->SerialNumber, CCB->Status); - } + { + if (CCB->Status == BusLogic_CCB_Active || + CCB->Status == BusLogic_CCB_Reset) + { + /* + Save the Completion Code for this CCB and queue the CCB + for completion processing. + */ + CCB->CompletionCode = CompletionCode; + BusLogic_QueueCompletedCCB(CCB); + } + else + { + /* + If a CCB ever appears in an Incoming Mailbox and is not marked + as status Active or Reset, then there is most likely a bug in + the Host Adapter firmware. + */ + BusLogic_Warning("Illegal CCB #%ld status %d in " + "Incoming Mailbox\n", HostAdapter, + CCB->SerialNumber, CCB->Status); + } + } NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree; if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox) NextIncomingMailbox = HostAdapter->FirstIncomingMailbox; @@ -3018,25 +3045,23 @@ /* - BusLogic_ProcessCompletedCCBs iterates over the completed CCBs setting - the SCSI Command Result Codes, deallocating the CCBs, and calling the - SCSI Subsystem Completion Routines. Interrupts should already have been - disabled by the caller. + BusLogic_ProcessCompletedCCBs iterates over the completed CCBs for Host + Adapter setting the SCSI Command Result Codes, deallocating the CCBs, and + calling the SCSI Subsystem Completion Routines. The Host Adapter's Lock + should already have been acquired by the caller. */ -static void BusLogic_ProcessCompletedCCBs(void) +static void BusLogic_ProcessCompletedCCBs(BusLogic_HostAdapter_T *HostAdapter) { - static boolean ProcessCompletedCCBsActive = false; - if (ProcessCompletedCCBsActive) return; - ProcessCompletedCCBsActive = true; - while (BusLogic_FirstCompletedCCB != NULL) + if (HostAdapter->ProcessCompletedCCBsActive) return; + HostAdapter->ProcessCompletedCCBsActive = true; + while (HostAdapter->FirstCompletedCCB != NULL) { - BusLogic_CCB_T *CCB = BusLogic_FirstCompletedCCB; + BusLogic_CCB_T *CCB = HostAdapter->FirstCompletedCCB; SCSI_Command_T *Command = CCB->Command; - BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter; - BusLogic_FirstCompletedCCB = CCB->Next; - if (BusLogic_FirstCompletedCCB == NULL) - BusLogic_LastCompletedCCB = NULL; + HostAdapter->FirstCompletedCCB = CCB->Next; + if (HostAdapter->FirstCompletedCCB == NULL) + HostAdapter->LastCompletedCCB = NULL; /* Process the Completed CCB. */ @@ -3047,10 +3072,9 @@ "%d Completed\n", HostAdapter, CCB->SerialNumber, TargetID); BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID] - .BusDeviceResetsCompleted); + &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsCompleted); + HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false; HostAdapter->CommandsSinceReset[TargetID] = 0; - HostAdapter->TaggedQueuingActive[TargetID] = false; HostAdapter->LastResetCompleted[TargetID] = jiffies; /* Place CCB back on the Host Adapter's free list. @@ -3101,16 +3125,17 @@ HostAdapter, CCB->SerialNumber, CCB->TargetID); break; case BusLogic_CommandCompletedWithoutError: - HostAdapter->TargetDeviceStatistics[CCB->TargetID] + HostAdapter->TargetStatistics[CCB->TargetID] .CommandsCompleted++; - HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true; + HostAdapter->TargetFlags[CCB->TargetID] + .CommandSuccessfulFlag = true; Command->result = DID_OK << 16; break; case BusLogic_CommandAbortedAtHostRequest: BusLogic_Warning("CCB #%ld to Target %d Aborted\n", HostAdapter, CCB->SerialNumber, CCB->TargetID); BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[CCB->TargetID] + &HostAdapter->TargetStatistics[CCB->TargetID] .CommandAbortsCompleted); Command->result = DID_ABORT << 16; break; @@ -3121,9 +3146,9 @@ CCB->TargetDeviceStatus); if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout) { - HostAdapter->TargetDeviceStatistics[CCB->TargetID] + HostAdapter->TargetStatistics[CCB->TargetID] .CommandsCompleted++; - if (BusLogic_GlobalOptions.Bits.TraceErrors) + if (BusLogic_GlobalOptions.TraceErrors) { int i; BusLogic_Notice("CCB #%ld Target %d: Result %X Host " @@ -3147,6 +3172,22 @@ break; } /* + When an INQUIRY command completes normally, save the + CmdQue (Tagged Queuing Supported) and WBus16 (16 Bit + Wide Data Transfers Supported) bits. + */ + if (CCB->CDB[0] == INQUIRY && CCB->CDB[1] == 0 && + CCB->HostAdapterStatus == BusLogic_CommandCompletedNormally) + { + BusLogic_TargetFlags_T *TargetFlags = + &HostAdapter->TargetFlags[CCB->TargetID]; + SCSI_Inquiry_T *InquiryResult = + (SCSI_Inquiry_T *) Command->request_buffer; + TargetFlags->TargetExists = true; + TargetFlags->TaggedQueuingSupported = InquiryResult->CmdQue; + TargetFlags->WideTransfersSupported = InquiryResult->WBus16; + } + /* Place CCB back on the Host Adapter's free list. */ BusLogic_DeallocateCCB(CCB); @@ -3156,7 +3197,7 @@ Command->scsi_done(Command); } } - ProcessCompletedCCBsActive = false; + HostAdapter->ProcessCompletedCCBsActive = false; } @@ -3169,96 +3210,84 @@ void *DeviceIdentifier, Registers_T *InterruptRegisters) { - BusLogic_HostAdapter_T *FirstHostAdapter = - BusLogic_RegisteredHostAdapters[IRQ_Channel]; - boolean HostAdapterResetRequested = false; - BusLogic_HostAdapter_T *HostAdapter; - BusLogic_Lock_T Lock; + BusLogic_HostAdapter_T *HostAdapter = + (BusLogic_HostAdapter_T *) DeviceIdentifier; + ProcessorFlags_T ProcessorFlags; /* - Iterate over the installed BusLogic Host Adapters accepting any Incoming - Mailbox entries and saving the completed CCBs for processing. This - interrupt handler is installed as a fast interrupt, so interrupts are - disabled when the interrupt handler is entered. + Acquire exclusive access to Host Adapter. */ - for (HostAdapter = FirstHostAdapter; - HostAdapter != NULL; - HostAdapter = HostAdapter->Next) + BusLogic_AcquireHostAdapterLockIH(HostAdapter, &ProcessorFlags); + /* + Handle Interrupts appropriately for each Host Adapter type. + */ + if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) { + BusLogic_InterruptRegister_T InterruptRegister; /* - Acquire exclusive access to Host Adapter. + Read the Host Adapter Interrupt Register. */ - BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock); - /* - Handle Interrupts appropriately for each Host Adapter type. - */ - if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) + InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter); + if (InterruptRegister.Bits.InterruptValid) { - BusLogic_InterruptRegister_T InterruptRegister; /* - Read the Host Adapter Interrupt Register. + Acknowledge the interrupt and reset the Host Adapter + Interrupt Register. */ - InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter); - if (InterruptRegister.Bits.InterruptValid) - { - /* - Acknowledge the interrupt and reset the Host Adapter - Interrupt Register. - */ - BusLogic_InterruptReset(HostAdapter); - /* - Process valid External SCSI Bus Reset and Incoming Mailbox - Loaded Interrupts. Command Complete Interrupts are noted, - and Outgoing Mailbox Available Interrupts are ignored, as - they are never enabled. - */ - if (InterruptRegister.Bits.ExternalBusReset) - { - HostAdapter->HostAdapterResetRequested = true; - HostAdapterResetRequested = true; - } - else if (InterruptRegister.Bits.IncomingMailboxLoaded) - BusLogic_ScanIncomingMailboxes(HostAdapter); - else if (InterruptRegister.Bits.CommandComplete) - HostAdapter->HostAdapterCommandCompleted = true; - } - } - else - { + BusLogic_InterruptReset(HostAdapter); /* - Check if there is a pending interrupt for this Host Adapter. + Process valid External SCSI Bus Reset and Incoming Mailbox + Loaded Interrupts. Command Complete Interrupts are noted, + and Outgoing Mailbox Available Interrupts are ignored, as + they are never enabled. */ - if (FlashPoint_InterruptPending(HostAdapter->CardHandle)) - if (FlashPoint_HandleInterrupt(HostAdapter->CardHandle) - == FlashPoint_ExternalBusReset) - { - HostAdapter->HostAdapterResetRequested = true; - HostAdapterResetRequested = true; - } + if (InterruptRegister.Bits.ExternalBusReset) + HostAdapter->HostAdapterExternalReset = true; + else if (InterruptRegister.Bits.IncomingMailboxLoaded) + BusLogic_ScanIncomingMailboxes(HostAdapter); + else if (InterruptRegister.Bits.CommandComplete) + HostAdapter->HostAdapterCommandCompleted = true; } + } + else + { /* - Release exclusive access to Host Adapter. + Check if there is a pending interrupt for this Host Adapter. */ - BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock); + if (FlashPoint_InterruptPending(HostAdapter->CardHandle)) + switch (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)) + { + case FlashPoint_NormalInterrupt: + break; + case FlashPoint_ExternalBusReset: + HostAdapter->HostAdapterExternalReset = true; + break; + case FlashPoint_InternalError: + BusLogic_Warning("Internal FlashPoint Error detected" + " - Resetting Host Adapter\n", HostAdapter); + HostAdapter->HostAdapterInternalError = true; + break; + } } /* Process any completed CCBs. */ - if (BusLogic_FirstCompletedCCB != NULL) - BusLogic_ProcessCompletedCCBs(); + if (HostAdapter->FirstCompletedCCB != NULL) + BusLogic_ProcessCompletedCCBs(HostAdapter); /* - Iterate over the Host Adapters performing any requested - Host Adapter Resets. + Reset the Host Adapter if requested. */ - if (HostAdapterResetRequested) - for (HostAdapter = FirstHostAdapter; - HostAdapter != NULL; - HostAdapter = HostAdapter->Next) - if (HostAdapter->HostAdapterResetRequested) - { - BusLogic_ResetHostAdapter(HostAdapter, NULL, 0); - HostAdapter->HostAdapterResetRequested = false; - scsi_mark_host_reset(HostAdapter->SCSI_Host); - } + if (HostAdapter->HostAdapterExternalReset || + HostAdapter->HostAdapterInternalError) + { + BusLogic_ResetHostAdapter(HostAdapter, NULL, 0); + HostAdapter->HostAdapterExternalReset = false; + HostAdapter->HostAdapterInternalError = false; + scsi_mark_host_reset(HostAdapter->SCSI_Host); + } + /* + Release exclusive access to Host Adapter. + */ + BusLogic_ReleaseHostAdapterLockIH(HostAdapter, &ProcessorFlags); } @@ -3293,8 +3322,7 @@ { HostAdapter->ActiveCommands[CCB->TargetID]++; if (CCB->Opcode != BusLogic_BusDeviceReset) - HostAdapter->TargetDeviceStatistics[CCB->TargetID] - .CommandsAttempted++; + HostAdapter->TargetStatistics[CCB->TargetID].CommandsAttempted++; } return true; } @@ -3312,8 +3340,10 @@ { BusLogic_HostAdapter_T *HostAdapter = (BusLogic_HostAdapter_T *) Command->host->hostdata; - BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics = - HostAdapter->TargetDeviceStatistics; + BusLogic_TargetFlags_T *TargetFlags = + &HostAdapter->TargetFlags[Command->target]; + BusLogic_TargetStatistics_T *TargetStatistics = + HostAdapter->TargetStatistics; unsigned char *CDB = Command->cmnd; int CDB_Length = Command->cmd_len; int TargetID = Command->target; @@ -3321,7 +3351,7 @@ void *BufferPointer = Command->request_buffer; int BufferLength = Command->request_bufflen; int SegmentCount = Command->use_sg; - BusLogic_Lock_T Lock; + ProcessorFlags_T ProcessorFlags; BusLogic_CCB_T *CCB; /* SCSI REQUEST_SENSE commands will be executed automatically by the Host @@ -3337,7 +3367,7 @@ /* Acquire exclusive access to Host Adapter. */ - BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags); /* Allocate a CCB from the Host Adapter's free list. In the unlikely event that there are none available and memory allocation fails, wait 1 second @@ -3371,13 +3401,9 @@ int Segment; CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather; CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T); -#ifndef CONFIG_SCSI_OMIT_FLASHPOINT if (BusLogic_MultiMasterHostAdapterP(HostAdapter)) CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList); - else CCB->DataPointer = (BusLogic_BusAddress_T) CCB->ScatterGatherList; -#else - CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList); -#endif + else CCB->DataPointer = Virtual_to_32Bit_Virtual(CCB->ScatterGatherList); for (Segment = 0; Segment < SegmentCount; Segment++) { CCB->ScatterGatherList[Segment].SegmentByteCount = @@ -3391,22 +3417,20 @@ case READ_6: case READ_10: CCB->DataDirection = BusLogic_DataInLengthChecked; - TargetDeviceStatistics[TargetID].ReadCommands++; + TargetStatistics[TargetID].ReadCommands++; BusLogic_IncrementByteCounter( - &TargetDeviceStatistics[TargetID].TotalBytesRead, BufferLength); + &TargetStatistics[TargetID].TotalBytesRead, BufferLength); BusLogic_IncrementSizeBucket( - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets, - BufferLength); + TargetStatistics[TargetID].ReadCommandSizeBuckets, BufferLength); break; case WRITE_6: case WRITE_10: CCB->DataDirection = BusLogic_DataOutLengthChecked; - TargetDeviceStatistics[TargetID].WriteCommands++; + TargetStatistics[TargetID].WriteCommands++; BusLogic_IncrementByteCounter( - &TargetDeviceStatistics[TargetID].TotalBytesWritten, BufferLength); + &TargetStatistics[TargetID].TotalBytesWritten, BufferLength); BusLogic_IncrementSizeBucket( - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets, - BufferLength); + TargetStatistics[TargetID].WriteCommandSizeBuckets, BufferLength); break; default: CCB->DataDirection = BusLogic_UncheckedDataTransfer; @@ -3434,20 +3458,18 @@ necessary to wait until there are no pending commands for a target device before queuing tagged commands. */ - HostAdapter->TaggedQueuingSupported[TargetID] = - Command->device->tagged_supported; if (HostAdapter->CommandsSinceReset[TargetID]++ >= BusLogic_MaxTaggedQueueDepth && - !HostAdapter->TaggedQueuingActive[TargetID] && + !TargetFlags->TaggedQueuingActive && HostAdapter->ActiveCommands[TargetID] == 0 && - HostAdapter->TaggedQueuingSupported[TargetID] && + TargetFlags->TaggedQueuingSupported && (HostAdapter->TaggedQueuingPermitted & (1 << TargetID))) { - HostAdapter->TaggedQueuingActive[TargetID] = true; + TargetFlags->TaggedQueuingActive = true; BusLogic_Notice("Tagged Queuing now active for Target %d\n", HostAdapter, TargetID); } - if (HostAdapter->TaggedQueuingActive[TargetID]) + if (TargetFlags->TaggedQueuingActive) { BusLogic_QueueTag_T QueueTag = BusLogic_SimpleQueueTag; /* @@ -3457,7 +3479,7 @@ write nearer the head position continue to arrive without interruption. Therefore, for each Target Device this driver keeps track of the last time either the queue was empty or an Ordered Queue Tag was issued. If - more than 3 seconds (one fifth of the 15 second disk timeout) have + more than 4 seconds (one fifth of the 20 second disk timeout) have elapsed since this last sequence point, this command will be issued with an Ordered Queue Tag rather than a Simple Queue Tag, which forces the Target Device to complete all previously queued commands before @@ -3465,7 +3487,7 @@ */ if (HostAdapter->ActiveCommands[TargetID] == 0) HostAdapter->LastSequencePoint[TargetID] = jiffies; - else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 3*HZ) + else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 4*HZ) { HostAdapter->LastSequencePoint[TargetID] = jiffies; QueueTag = BusLogic_OrderedQueueTag; @@ -3519,20 +3541,20 @@ */ CCB->Status = BusLogic_CCB_Active; HostAdapter->ActiveCommands[TargetID]++; - TargetDeviceStatistics[TargetID].CommandsAttempted++; + TargetStatistics[TargetID].CommandsAttempted++; FlashPoint_StartCCB(HostAdapter->CardHandle, CCB); /* The Command may have already completed and BusLogic_QueueCompletedCCB been called, or it may still be pending. */ if (CCB->Status == BusLogic_CCB_Completed) - BusLogic_ProcessCompletedCCBs(); + BusLogic_ProcessCompletedCCBs(HostAdapter); } /* Release exclusive access to Host Adapter. */ Done: - BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags); return 0; } @@ -3546,15 +3568,15 @@ BusLogic_HostAdapter_T *HostAdapter = (BusLogic_HostAdapter_T *) Command->host->hostdata; int TargetID = Command->target; - BusLogic_Lock_T Lock; + ProcessorFlags_T ProcessorFlags; BusLogic_CCB_T *CCB; int Result; BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID].CommandAbortsRequested); + &HostAdapter->TargetStatistics[TargetID].CommandAbortsRequested); /* Acquire exclusive access to Host Adapter. */ - BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags); /* If this Command has already completed, then no Abort is necessary. */ @@ -3604,7 +3626,7 @@ Firmware version 5.xx does generate Abort Tag messages, so it is possible to abort commands when Tagged Queuing is active. */ - if (HostAdapter->TaggedQueuingActive[TargetID] && + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && HostAdapter->FirmwareVersion[0] < '5') { BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - " @@ -3618,8 +3640,7 @@ BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID); BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID] - .CommandAbortsAttempted); + &HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted); Result = SCSI_ABORT_PENDING; } else @@ -3638,7 +3659,7 @@ BusLogic_Warning("Aborting CCB #%ld to Target %d\n", HostAdapter, CCB->SerialNumber, TargetID); BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID].CommandAbortsAttempted); + &HostAdapter->TargetStatistics[TargetID].CommandAbortsAttempted); FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB); /* The Abort may have already been completed and @@ -3648,7 +3669,7 @@ Result = SCSI_ABORT_PENDING; if (CCB->Status == BusLogic_CCB_Completed) { - BusLogic_ProcessCompletedCCBs(); + BusLogic_ProcessCompletedCCBs(HostAdapter); Result = SCSI_ABORT_SUCCESS; } } @@ -3656,7 +3677,7 @@ Release exclusive access to Host Adapter. */ Done: - BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags); return Result; } @@ -3670,18 +3691,31 @@ SCSI_Command_T *Command, unsigned int ResetFlags) { - BusLogic_Lock_T Lock; + ProcessorFlags_T ProcessorFlags; BusLogic_CCB_T *CCB; int TargetID, Result; - if (Command == NULL) - BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets); - else BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[Command->target] - .HostAdapterResetsRequested); + boolean HardReset; + if (HostAdapter->HostAdapterExternalReset) + { + BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets); + HardReset = false; + } + else if (HostAdapter->HostAdapterInternalError) + { + BusLogic_IncrementErrorCounter(&HostAdapter->HostAdapterInternalErrors); + HardReset = true; + } + else + { + BusLogic_IncrementErrorCounter( + &HostAdapter->TargetStatistics[Command->target] + .HostAdapterResetsRequested); + HardReset = true; + } /* Acquire exclusive access to Host Adapter. */ - BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags); /* If this is an Asynchronous Reset and this Command has already completed, then no Reset is necessary. @@ -3723,20 +3757,25 @@ } } if (Command == NULL) - BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n", - HostAdapter, HostAdapter->FullModelName); + { + if (HostAdapter->HostAdapterInternalError) + BusLogic_Warning("Resetting %s due to Host Adapter Internal Error\n", + HostAdapter, HostAdapter->FullModelName); + else BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n", + HostAdapter, HostAdapter->FullModelName); + } else { BusLogic_Warning("Resetting %s due to Target %d\n", HostAdapter, HostAdapter->FullModelName, Command->target); BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[Command->target] + &HostAdapter->TargetStatistics[Command->target] .HostAdapterResetsAttempted); } /* Attempt to Reset and Reinitialize the Host Adapter. */ - if (!(BusLogic_HardResetHostAdapter(HostAdapter) && + if (!(BusLogic_HardwareResetHostAdapter(HostAdapter, HardReset) && BusLogic_InitializeHostAdapter(HostAdapter))) { BusLogic_Error("Resetting %s Failed\n", HostAdapter, @@ -3746,7 +3785,7 @@ } if (Command != NULL) BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[Command->target] + &HostAdapter->TargetStatistics[Command->target] .HostAdapterResetsCompleted); /* Mark all currently executing CCBs as having been Reset. @@ -3761,7 +3800,8 @@ Note that a timer interrupt may occur here, but all active CCBs have already been marked Reset and so a reentrant call will return Pending. */ - BusLogic_Delay(HostAdapter->BusSettleTime); + if (HardReset) + BusLogic_Delay(HostAdapter->BusSettleTime); /* If this is a Synchronous Reset, perform completion processing for the Command being Reset. @@ -3798,7 +3838,7 @@ Release exclusive access to Host Adapter. */ Done: - BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags); return Result; } @@ -3814,14 +3854,14 @@ { int TargetID = Command->target; BusLogic_CCB_T *CCB, *XCCB; - BusLogic_Lock_T Lock; + ProcessorFlags_T ProcessorFlags; int Result = -1; BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID].BusDeviceResetsRequested); + &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsRequested); /* Acquire exclusive access to Host Adapter. */ - BusLogic_AcquireHostAdapterLock(HostAdapter, &Lock); + BusLogic_AcquireHostAdapterLock(HostAdapter, &ProcessorFlags); /* If this is an Asynchronous Reset and this Command has already completed, then no Reset is necessary. @@ -3891,7 +3931,7 @@ while there are tagged commands outstanding. Therefore, in that case a full Host Adapter Hard Reset and SCSI Bus Reset must be done. */ - if (HostAdapter->TaggedQueuingActive[TargetID] && + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && HostAdapter->ActiveCommands[TargetID] > 0 && HostAdapter->FirmwareVersion[0] < '5') goto Done; @@ -3958,7 +3998,7 @@ processing performed. */ BusLogic_IncrementErrorCounter( - &HostAdapter->TargetDeviceStatistics[TargetID].BusDeviceResetsAttempted); + &HostAdapter->TargetStatistics[TargetID].BusDeviceResetsAttempted); HostAdapter->BusDeviceResetPendingCCB[TargetID] = CCB; HostAdapter->LastResetAttempted[TargetID] = jiffies; for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll) @@ -3973,7 +4013,7 @@ if (BusLogic_FlashPointHostAdapterP(HostAdapter)) if (CCB->Status == BusLogic_CCB_Completed) { - BusLogic_ProcessCompletedCCBs(); + BusLogic_ProcessCompletedCCBs(HostAdapter); Result = SCSI_RESET_SUCCESS; } /* @@ -3986,7 +4026,7 @@ /* Release exclusive access to Host Adapter. */ - BusLogic_ReleaseHostAdapterLock(HostAdapter, &Lock); + BusLogic_ReleaseHostAdapterLock(HostAdapter, &ProcessorFlags); return Result; } @@ -4007,11 +4047,11 @@ it has been less than 10 minutes since the last reset occurred, or since the system was initialized if no prior resets have occurred. */ - if (HostAdapter->TaggedQueuingActive[TargetID] && + if (HostAdapter->TargetFlags[TargetID].TaggedQueuingActive && jiffies - HostAdapter->LastResetCompleted[TargetID] < 10*60*HZ) { HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID); - HostAdapter->TaggedQueuingActive[TargetID] = false; + HostAdapter->TargetFlags[TargetID].TaggedQueuingActive = false; BusLogic_Warning("Tagged Queuing now disabled for Target %d\n", HostAdapter, TargetID); } @@ -4032,10 +4072,10 @@ forcing a Hard Reset before the Bus Device Reset has had a chance to clear the error condition. */ - if (HostAdapter->CommandSuccessfulFlag[TargetID] || + if (HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag || jiffies - HostAdapter->LastResetAttempted[TargetID] < HZ/10) { - HostAdapter->CommandSuccessfulFlag[TargetID] = false; + HostAdapter->TargetFlags[TargetID].CommandSuccessfulFlag = false; return BusLogic_SendBusDeviceReset(HostAdapter, Command, ResetFlags); } /* Fall through to Hard Reset case. */ @@ -4076,16 +4116,18 @@ struct buffer_head *BufferHead; if (HostAdapter->ExtendedTranslationEnabled && Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */) - if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */) - { - DiskParameters->Heads = 255; - DiskParameters->Sectors = 63; - } - else - { - DiskParameters->Heads = 128; - DiskParameters->Sectors = 32; - } + { + if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */) + { + DiskParameters->Heads = 255; + DiskParameters->Sectors = 63; + } + else + { + DiskParameters->Heads = 128; + DiskParameters->Sectors = 32; + } + } else { DiskParameters->Heads = 64; @@ -4105,24 +4147,28 @@ */ if (*(unsigned short *) (BufferHead->b_data + 0x1FE) == 0xAA55) { - struct partition *PartitionEntry = - (struct partition *) (BufferHead->b_data + 0x1BE); + PartitionTable_T *FirstPartitionEntry = + (PartitionTable_T *) (BufferHead->b_data + 0x1BE); + PartitionTable_T *PartitionEntry = FirstPartitionEntry; int SavedCylinders = DiskParameters->Cylinders, PartitionNumber; + unsigned char PartitionEntryEndHead, PartitionEntryEndSector; for (PartitionNumber = 0; PartitionNumber < 4; PartitionNumber++) { - if (PartitionEntry->end_head == 64-1) + PartitionEntryEndHead = PartitionEntry->end_head; + PartitionEntryEndSector = PartitionEntry->end_sector & 0x3F; + if (PartitionEntryEndHead == 64-1) { DiskParameters->Heads = 64; DiskParameters->Sectors = 32; break; } - else if (PartitionEntry->end_head == 128-1) + else if (PartitionEntryEndHead == 128-1) { DiskParameters->Heads = 128; DiskParameters->Sectors = 32; break; } - else if (PartitionEntry->end_head == 255-1) + else if (PartitionEntryEndHead == 255-1) { DiskParameters->Heads = 255; DiskParameters->Sectors = 63; @@ -4130,14 +4176,29 @@ } PartitionEntry++; } + if (PartitionNumber == 4) + { + PartitionEntryEndHead = FirstPartitionEntry->end_head; + PartitionEntryEndSector = FirstPartitionEntry->end_sector & 0x3F; + } DiskParameters->Cylinders = Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors); - if (SavedCylinders != DiskParameters->Cylinders) + if (PartitionNumber < 4 && + PartitionEntryEndSector == DiskParameters->Sectors) { - BusLogic_Warning("Warning: Extended Translation Setting " - "(> 1GB Switch) does not match\n", HostAdapter); - BusLogic_Warning("Partition Table - Adopting %d/%d Geometry " - "from Partition Table\n", HostAdapter, + if (DiskParameters->Cylinders != SavedCylinders) + BusLogic_Warning("Adopting Geometry %d/%d from Partition Table\n", + HostAdapter, + DiskParameters->Heads, DiskParameters->Sectors); + } + else if (PartitionEntryEndHead > 0 || PartitionEntryEndSector > 0) + { + BusLogic_Warning("Warning: Partition Table appears to " + "have Geometry %d/%d which is\n", HostAdapter, + PartitionEntryEndHead + 1, + PartitionEntryEndSector); + BusLogic_Warning("not compatible with current BusLogic " + "Host Adapter Geometry %d/%d\n", HostAdapter, DiskParameters->Heads, DiskParameters->Sectors); } } @@ -4155,22 +4216,21 @@ int HostNumber, int WriteFlag) { BusLogic_HostAdapter_T *HostAdapter; - BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics; - int IRQ_Channel, TargetID, Length; + BusLogic_TargetStatistics_T *TargetStatistics; + int TargetID, Length; char *Buffer; if (WriteFlag) return 0; - for (IRQ_Channel = 0; IRQ_Channel < NR_IRQS; IRQ_Channel++) + for (HostAdapter = BusLogic_FirstRegisteredHostAdapter; + HostAdapter != NULL; + HostAdapter = HostAdapter->Next) + if (HostAdapter->HostNumber == HostNumber) break; + if (HostAdapter == NULL) { - HostAdapter = BusLogic_RegisteredHostAdapters[IRQ_Channel]; - while (HostAdapter != NULL) - { - if (HostAdapter->HostNumber == HostNumber) break; - HostAdapter = HostAdapter->Next; - } - if (HostAdapter != NULL) break; + BusLogic_Error("Cannot find Host Adapter for SCSI Host %d\n", + NULL, HostNumber); + return 0; } - if (HostAdapter == NULL) return -1; - TargetDeviceStatistics = HostAdapter->TargetDeviceStatistics; + TargetStatistics = HostAdapter->TargetStatistics; Buffer = HostAdapter->MessageBuffer; Length = HostAdapter->MessageBufferLength; Length += sprintf(&Buffer[Length], "\n\ @@ -4181,100 +4241,105 @@ Length += sprintf(&Buffer[Length], "\n\n\ DATA TRANSFER STATISTICS\n\ \n\ -Target Tagged Queuing Queue Depth Commands Attempted Commands Completed\n\ -====== ============== =========== ================== ==================\n"); +Target Tagged Queuing Queue Depth Active Attempted Completed\n\ +====== ============== =========== ====== ========= =========\n"); for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0) - { - Length += - sprintf(&Buffer[Length], " %2d %s", TargetID, - (HostAdapter->TaggedQueuingSupported[TargetID] - ? (HostAdapter->TaggedQueuingActive[TargetID] - ? " Active" - : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID) - ? " Permitted" : " Disabled")) - : "Not Supported")); - Length += sprintf(&Buffer[Length], - " %3d %9u %9u\n", - HostAdapter->QueueDepth[TargetID], - TargetDeviceStatistics[TargetID].CommandsAttempted, - TargetDeviceStatistics[TargetID].CommandsCompleted); - } + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) continue; + Length += + sprintf(&Buffer[Length], " %2d %s", TargetID, + (TargetFlags->TaggedQueuingSupported + ? (TargetFlags->TaggedQueuingActive + ? " Active" + : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID) + ? " Permitted" : " Disabled")) + : "Not Supported")); + Length += sprintf(&Buffer[Length], + " %3d %3u %9u %9u\n", + HostAdapter->QueueDepth[TargetID], + HostAdapter->ActiveCommands[TargetID], + TargetStatistics[TargetID].CommandsAttempted, + TargetStatistics[TargetID].CommandsCompleted); + } Length += sprintf(&Buffer[Length], "\n\ Target Read Commands Write Commands Total Bytes Read Total Bytes Written\n\ ====== ============= ============== =================== ===================\n"); for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0) - { + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) continue; + Length += + sprintf(&Buffer[Length], " %2d %9u %9u", TargetID, + TargetStatistics[TargetID].ReadCommands, + TargetStatistics[TargetID].WriteCommands); + if (TargetStatistics[TargetID].TotalBytesRead.Billions > 0) Length += - sprintf(&Buffer[Length], " %2d %9u %9u", TargetID, - TargetDeviceStatistics[TargetID].ReadCommands, - TargetDeviceStatistics[TargetID].WriteCommands); - if (TargetDeviceStatistics[TargetID].TotalBytesRead.Billions > 0) - Length += - sprintf(&Buffer[Length], " %9u%09u", - TargetDeviceStatistics[TargetID].TotalBytesRead.Billions, - TargetDeviceStatistics[TargetID].TotalBytesRead.Units); - else - Length += - sprintf(&Buffer[Length], " %9u", - TargetDeviceStatistics[TargetID].TotalBytesRead.Units); - if (TargetDeviceStatistics[TargetID].TotalBytesWritten.Billions > 0) - Length += - sprintf(&Buffer[Length], " %9u%09u\n", - TargetDeviceStatistics[TargetID].TotalBytesWritten.Billions, - TargetDeviceStatistics[TargetID].TotalBytesWritten.Units); - else - Length += - sprintf(&Buffer[Length], " %9u\n", - TargetDeviceStatistics[TargetID].TotalBytesWritten.Units); - } + sprintf(&Buffer[Length], " %9u%09u", + TargetStatistics[TargetID].TotalBytesRead.Billions, + TargetStatistics[TargetID].TotalBytesRead.Units); + else + Length += + sprintf(&Buffer[Length], " %9u", + TargetStatistics[TargetID].TotalBytesRead.Units); + if (TargetStatistics[TargetID].TotalBytesWritten.Billions > 0) + Length += + sprintf(&Buffer[Length], " %9u%09u\n", + TargetStatistics[TargetID].TotalBytesWritten.Billions, + TargetStatistics[TargetID].TotalBytesWritten.Units); + else + Length += + sprintf(&Buffer[Length], " %9u\n", + TargetStatistics[TargetID].TotalBytesWritten.Units); + } Length += sprintf(&Buffer[Length], "\n\ Target Command 0-1KB 1-2KB 2-4KB 4-8KB 8-16KB\n\ ====== ======= ========= ========= ========= ========= =========\n"); for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0) - { - Length += - sprintf(&Buffer[Length], - " %2d Read %9u %9u %9u %9u %9u\n", TargetID, - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[0], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[1], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[2], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[3], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[4]); - Length += - sprintf(&Buffer[Length], - " %2d Write %9u %9u %9u %9u %9u\n", TargetID, - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[0], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[1], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[2], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[3], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[4]); - } + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) continue; + Length += + sprintf(&Buffer[Length], + " %2d Read %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].ReadCommandSizeBuckets[0], + TargetStatistics[TargetID].ReadCommandSizeBuckets[1], + TargetStatistics[TargetID].ReadCommandSizeBuckets[2], + TargetStatistics[TargetID].ReadCommandSizeBuckets[3], + TargetStatistics[TargetID].ReadCommandSizeBuckets[4]); + Length += + sprintf(&Buffer[Length], + " %2d Write %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].WriteCommandSizeBuckets[0], + TargetStatistics[TargetID].WriteCommandSizeBuckets[1], + TargetStatistics[TargetID].WriteCommandSizeBuckets[2], + TargetStatistics[TargetID].WriteCommandSizeBuckets[3], + TargetStatistics[TargetID].WriteCommandSizeBuckets[4]); + } Length += sprintf(&Buffer[Length], "\n\ Target Command 16-32KB 32-64KB 64-128KB 128-256KB 256KB+\n\ ====== ======= ========= ========= ========= ========= =========\n"); for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0) - { - Length += - sprintf(&Buffer[Length], - " %2d Read %9u %9u %9u %9u %9u\n", TargetID, - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[5], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[6], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[7], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[8], - TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[9]); - Length += - sprintf(&Buffer[Length], - " %2d Write %9u %9u %9u %9u %9u\n", TargetID, - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[5], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[6], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[7], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[8], - TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[9]); - } + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) continue; + Length += + sprintf(&Buffer[Length], + " %2d Read %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].ReadCommandSizeBuckets[5], + TargetStatistics[TargetID].ReadCommandSizeBuckets[6], + TargetStatistics[TargetID].ReadCommandSizeBuckets[7], + TargetStatistics[TargetID].ReadCommandSizeBuckets[8], + TargetStatistics[TargetID].ReadCommandSizeBuckets[9]); + Length += + sprintf(&Buffer[Length], + " %2d Write %9u %9u %9u %9u %9u\n", TargetID, + TargetStatistics[TargetID].WriteCommandSizeBuckets[5], + TargetStatistics[TargetID].WriteCommandSizeBuckets[6], + TargetStatistics[TargetID].WriteCommandSizeBuckets[7], + TargetStatistics[TargetID].WriteCommandSizeBuckets[8], + TargetStatistics[TargetID].WriteCommandSizeBuckets[9]); + } Length += sprintf(&Buffer[Length], "\n\n\ ERROR RECOVERY STATISTICS\n\ \n\ @@ -4283,21 +4348,26 @@ ID \\\\\\\\ Attempted //// \\\\\\\\ Attempted //// \\\\\\\\ Attempted ////\n\ ====== ===== ===== ===== ===== ===== ===== ===== ===== =====\n"); for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++) - if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0) + { + BusLogic_TargetFlags_T *TargetFlags = &HostAdapter->TargetFlags[TargetID]; + if (!TargetFlags->TargetExists) continue; Length += sprintf(&Buffer[Length], "\ %2d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n", TargetID, - TargetDeviceStatistics[TargetID].CommandAbortsRequested, - TargetDeviceStatistics[TargetID].CommandAbortsAttempted, - TargetDeviceStatistics[TargetID].CommandAbortsCompleted, - TargetDeviceStatistics[TargetID].BusDeviceResetsRequested, - TargetDeviceStatistics[TargetID].BusDeviceResetsAttempted, - TargetDeviceStatistics[TargetID].BusDeviceResetsCompleted, - TargetDeviceStatistics[TargetID].HostAdapterResetsRequested, - TargetDeviceStatistics[TargetID].HostAdapterResetsAttempted, - TargetDeviceStatistics[TargetID].HostAdapterResetsCompleted); + TargetStatistics[TargetID].CommandAbortsRequested, + TargetStatistics[TargetID].CommandAbortsAttempted, + TargetStatistics[TargetID].CommandAbortsCompleted, + TargetStatistics[TargetID].BusDeviceResetsRequested, + TargetStatistics[TargetID].BusDeviceResetsAttempted, + TargetStatistics[TargetID].BusDeviceResetsCompleted, + TargetStatistics[TargetID].HostAdapterResetsRequested, + TargetStatistics[TargetID].HostAdapterResetsAttempted, + TargetStatistics[TargetID].HostAdapterResetsCompleted); + } Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n", HostAdapter->ExternalHostAdapterResets); + Length += sprintf(&Buffer[Length], "Host Adapter Internal Errors: %d\n", + HostAdapter->HostAdapterInternalErrors); if (Length >= BusLogic_MessageBufferSize) BusLogic_Error("Message Buffer length %d exceeds size %d\n", HostAdapter, Length, BusLogic_MessageBufferSize); @@ -4339,17 +4409,22 @@ Buffer); HostAdapter->MessageBufferLength += Length; if (BeginningOfLine) - printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], - HostAdapter->HostNumber, Buffer); + { + if (Buffer[0] != '\n' || Length > 1) + printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], + HostAdapter->HostNumber, Buffer); + } else printk("%s", Buffer); } else { if (BeginningOfLine) - if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized) - printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], - HostAdapter->HostNumber, Buffer); - else printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer); + { + if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized) + printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel], + HostAdapter->HostNumber, Buffer); + else printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer); + } else printk("%s", Buffer); } BeginningOfLine = (Buffer[Length-1] == '\n'); @@ -4357,364 +4432,553 @@ /* - BusLogic_Setup handles processing of Kernel Command Line Arguments. + BusLogic_ParseKeyword parses an individual option keyword. It returns true + and updates the pointer if the keyword is recognized and false otherwise. +*/ - For the BusLogic driver, a Kernel Command Line Entry comprises the driver - identifier "BusLogic=" optionally followed by a comma-separated sequence of - integers and then optionally followed by a comma-separated sequence of - strings. Each command line entry applies to one BusLogic Host Adapter. - Multiple command line entries may be used in systems which contain multiple - BusLogic Host Adapters. +static boolean BusLogic_ParseKeyword(char **StringPointer, char *Keyword) +{ + char *Pointer = *StringPointer; + while (*Keyword != '\0') + { + char StringChar = *Pointer++; + char KeywordChar = *Keyword++; + if (StringChar >= 'A' && StringChar <= 'Z') + StringChar += 'a' - 'Z'; + if (KeywordChar >= 'A' && KeywordChar <= 'Z') + KeywordChar += 'a' - 'Z'; + if (StringChar != KeywordChar) return false; + } + *StringPointer = Pointer; + return true; +} - The first integer specified is the I/O Address at which the Host Adapter is - located. If unspecified, it defaults to 0 which means to apply this entry to - the first BusLogic Host Adapter found during the default probe sequence. If - any I/O Address parameters are provided on the command line, then the default - probe sequence is omitted. - - The second integer specified is the Tagged Queue Depth to use for Target - Devices that support Tagged Queuing. The Queue Depth is the number of SCSI - commands that are allowed to be concurrently presented for execution. If - unspecified, it defaults to 0 which means to use a value determined - automatically based on the Host Adapter's Total Queue Depth and the number, - type, speed, and capabilities of the detected Target Devices. For Host - Adapters that require ISA Bounce Buffers, the Tagged Queue Depth is - automatically set to BusLogic_TaggedQueueDepthBounceBuffers to avoid - excessive preallocation of DMA Bounce Buffer memory. Target Devices that do - not support Tagged Queuing use a Queue Depth of BusLogic_UntaggedQueueDepth. - - The third integer specified is the Bus Settle Time in seconds. This is - the amount of time to wait between a Host Adapter Hard Reset which initiates - a SCSI Bus Reset and issuing any SCSI Commands. If unspecified, it defaults - to 0 which means to use the value of BusLogic_DefaultBusSettleTime. - - The fourth integer specified is the Local Options. If unspecified, it - defaults to 0. Note that Local Options are only applied to a specific Host - Adapter. - The fifth integer specified is the Global Options. If unspecified, it - defaults to 0. Note that Global Options are applied across all Host - Adapters. +/* + BusLogic_ParseDriverOptions handles processing of BusLogic Driver Options + specifications. - The string options are used to provide control over Tagged Queuing, Error - Recovery, and Host Adapter Probing. + BusLogic Driver Options may be specified either via the Linux Kernel Command + Line or via the Loadable Kernel Module Installation Facility. Driver Options + for multiple host adapters may be specified either by separating the option + strings by a semicolon, or by specifying multiple "BusLogic=" strings on the + command line. Individual option specifications for a single host adapter are + separated by commas. The Probing and Debugging Options apply to all host + adapters whereas the remaining options apply individually only to the + selected host adapter. - The Tagged Queuing specification begins with "TQ:" and allows for explicitly - specifying whether Tagged Queuing is permitted on Target Devices that support - it. The following specification options are available: - - TQ:Default Tagged Queuing will be permitted based on the firmware - version of the BusLogic Host Adapter and based on - whether the Tagged Queue Depth value allows queuing - multiple commands. - - TQ:Enable Tagged Queuing will be enabled for all Target Devices - on this Host Adapter overriding any limitation that - would otherwise be imposed based on the Host Adapter - firmware version. - - TQ:Disable Tagged Queuing will be disabled for all Target Devices - on this Host Adapter. - - TQ: Tagged Queuing will be controlled individually for each - Target Device. is a sequence of "Y", - "N", and "X" characters. "Y" enabled Tagged Queuing, - "N" disables Tagged Queuing, and "X" accepts the - default based on the firmware version. The first - character refers to Target Device 0, the second to - Target Device 1, and so on; if the sequence of "Y", - "N", and "X" characters does not cover all the Target - Devices, unspecified characters are assumed to be "X". - - Note that explicitly requesting Tagged Queuing may lead to problems; this - facility is provided primarily to allow disabling Tagged Queuing on Target - Devices that do not implement it correctly. - - The Error Recovery Strategy specification begins with "ER:" and allows for - explicitly specifying the Error Recovery action to be performed when - ResetCommand is called due to a SCSI Command failing to complete - successfully. The following specification options are available: - - ER:Default Error Recovery will select between the Hard Reset and - Bus Device Reset options based on the recommendation - of the SCSI Subsystem. - - ER:HardReset Error Recovery will initiate a Host Adapter Hard Reset - which also causes a SCSI Bus Reset. - - ER:BusDeviceReset Error Recovery will send a Bus Device Reset message to - the individual Target Device causing the error. If - Error Recovery is again initiated for this Target - Device and no SCSI Command to this Target Device has - completed successfully since the Bus Device Reset - message was sent, then a Hard Reset will be attempted. - - ER:None Error Recovery will be suppressed. This option should - only be selected if a SCSI Bus Reset or Bus Device - Reset will cause the Target Device to fail completely - and unrecoverably. - - ER: Error Recovery will be controlled individually for each - Target Device. is a sequence of "D", - "H", "B", and "N" characters. "D" selects Default, "H" - selects Hard Reset, "B" selects Bus Device Reset, and - "N" selects None. The first character refers to Target - Device 0, the second to Target Device 1, and so on; if - the sequence of "D", "H", "B", and "N" characters does - not cover all the possible Target Devices, unspecified - characters are assumed to be "D". - - The Host Adapter Probing specification comprises the following strings: - - NoProbe No probing of any kind is to be performed, and hence - no BusLogic Host Adapters will be detected. - - NoProbeISA No probing of the standard ISA I/O Addresses will - be done, and hence only PCI MultiMaster and FlashPoint - Host Adapters will be detected. - - NoProbePCI No interrogation of PCI Configuration Space will be - made, and hence only ISA Multimaster Host Adapters - will be detected, as well as PCI Multimaster Host - Adapters that have their ISA Compatible I/O Port - set to "Primary" or "Alternate". - - NoSortPCI PCI MultiMaster Host Adapters will be enumerated in - the order provided by the PCI BIOS, ignoring any - setting of the AutoSCSI "Use Bus And Device # For PCI - Scanning Seq." option. - - MultiMasterFirst By default, if both FlashPoint and PCI MultiMaster - Host Adapters are present, this driver will probe for - FlashPoint Host Adapters first unless the BIOS primary - disk is controlled by the first PCI MultiMaster Host - Adapter, in which case MultiMaster Host Adapters will - be probed first. This option forces MultiMaster Host - Adapters to be probed first. - - FlashPointFirst By default, if both FlashPoint and PCI MultiMaster - Host Adapters are present, this driver will probe for - FlashPoint Host Adapters first unless the BIOS primary - disk is controlled by the first PCI MultiMaster Host - Adapter, in which case MultiMaster Host Adapters will - be probed first. This option forces FlashPoint Host - Adapters to be probed first. - - Debug Sets all the tracing bits in BusLogic_GlobalOptions. - -*/ - -void BusLogic_Setup(char *Strings, int *Integers) -{ - BusLogic_CommandLineEntry_T *CommandLineEntry = - &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++]; - int IntegerCount = Integers[0]; - int TargetID, i; - CommandLineEntry->IO_Address = 0; - CommandLineEntry->TaggedQueueDepth = 0; - CommandLineEntry->BusSettleTime = 0; - CommandLineEntry->TaggedQueuingPermitted = 0; - CommandLineEntry->TaggedQueuingPermittedMask = 0; - CommandLineEntry->LocalOptions.All = 0; - memset(CommandLineEntry->ErrorRecoveryStrategy, - BusLogic_ErrorRecovery_Default, - sizeof(CommandLineEntry->ErrorRecoveryStrategy)); - if (IntegerCount > 5) - BusLogic_Error("BusLogic: Unexpected Command Line Integers " - "ignored\n", NULL); - if (IntegerCount >= 1) - { - BusLogic_IO_Address_T IO_Address = Integers[1]; - if (IO_Address > 0) - { - BusLogic_ProbeInfo_T *ProbeInfo; - for (i = 0; ; i++) - if (BusLogic_ISA_StandardAddresses[i] == 0) - { - BusLogic_Error("BusLogic: Invalid Command Line Entry " - "(illegal I/O Address 0x%X)\n", - NULL, IO_Address); - return; - } - else if (i < BusLogic_ProbeInfoCount && - IO_Address == BusLogic_ProbeInfoList[i].IO_Address) - { - BusLogic_Error("BusLogic: Invalid Command Line Entry " - "(duplicate I/O Address 0x%X)\n", - NULL, IO_Address); - return; - } - else if (IO_Address >= 0x400 || - IO_Address == BusLogic_ISA_StandardAddresses[i]) break; - ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++]; - ProbeInfo->HostAdapterType = BusLogic_MultiMaster; - ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus; - } - CommandLineEntry->IO_Address = IO_Address; - } - if (IntegerCount >= 2) - { - unsigned short TaggedQueueDepth = Integers[2]; - if (TaggedQueueDepth > BusLogic_MaxTaggedQueueDepth) - { - BusLogic_Error("BusLogic: Invalid Command Line Entry " - "(illegal Tagged Queue Depth %d)\n", - NULL, TaggedQueueDepth); + The BusLogic Driver Probing Options comprise the following: + + IO: + + The "IO:" option specifies an ISA I/O Address to be probed for a non-PCI + MultiMaster Host Adapter. If neither "IO:" nor "NoProbeISA" options are + specified, then the standard list of BusLogic MultiMaster ISA I/O Addresses + will be probed (0x330, 0x334, 0x230, 0x234, 0x130, and 0x134). Multiple + "IO:" options may be specified to precisely determine the I/O Addresses to + be probed, but the probe order will always follow the standard list. + + NoProbe + + The "NoProbe" option disables all probing and therefore no BusLogic Host + Adapters will be detected. + + NoProbeISA + + The "NoProbeISA" option disables probing of the standard BusLogic ISA I/O + Addresses and therefore only PCI MultiMaster and FlashPoint Host Adapters + will be detected. + + NoProbePCI + + The "NoProbePCI" options disables the interrogation of PCI Configuration + Space and therefore only ISA Multimaster Host Adapters will be detected, as + well as PCI Multimaster Host Adapters that have their ISA Compatible I/O + Port set to "Primary" or "Alternate". + + NoSortPCI + + The "NoSortPCI" option forces PCI MultiMaster Host Adapters to be + enumerated in the order provided by the PCI BIOS, ignoring any setting of + the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option. + + MultiMasterFirst + + The "MultiMasterFirst" option forces MultiMaster Host Adapters to be probed + before FlashPoint Host Adapters. By default, if both FlashPoint and PCI + MultiMaster Host Adapters are present, this driver will probe for + FlashPoint Host Adapters first unless the BIOS primary disk is controlled + by the first PCI MultiMaster Host Adapter, in which case MultiMaster Host + Adapters will be probed first. + + FlashPointFirst + + The "FlashPointFirst" option forces FlashPoint Host Adapters to be probed + before MultiMaster Host Adapters. + + The BusLogic Driver Tagged Queuing Options allow for explicitly specifying + the Queue Depth and whether Tagged Queuing is permitted for each Target + Device (assuming that the Target Device supports Tagged Queuing). The Queue + Depth is the number of SCSI Commands that are allowed to be concurrently + presented for execution (either to the Host Adapter or Target Device). Note + that explicitly enabling Tagged Queuing may lead to problems; the option to + enable or disable Tagged Queuing is provided primarily to allow disabling + Tagged Queuing on Target Devices that do not implement it correctly. The + following options are available: + + QueueDepth: + + The "QueueDepth:" or QD:" option specifies the Queue Depth to use for all + Target Devices that support Tagged Queuing, as well as the maximum Queue + Depth for devices that do not support Tagged Queuing. If no Queue Depth + option is provided, the Queue Depth will be determined automatically based + on the Host Adapter's Total Queue Depth and the number, type, speed, and + capabilities of the detected Target Devices. For Host Adapters that + require ISA Bounce Buffers, the Queue Depth is automatically set by default + to BusLogic_TaggedQueueDepthBB or BusLogic_UntaggedQueueDepthBB to avoid + excessive preallocation of DMA Bounce Buffer memory. Target Devices that + do not support Tagged Queuing always have their Queue Depth set to + BusLogic_UntaggedQueueDepth or BusLogic_UntaggedQueueDepthBB, unless a + lower Queue Depth option is provided. A Queue Depth of 1 automatically + disables Tagged Queuing. + + QueueDepth:[,...] + + The "QueueDepth:[...]" or "QD:[...]" option specifies the Queue Depth + individually for each Target Device. If an is omitted, the + associated Target Device will have its Queue Depth selected automatically. + + TaggedQueuing:Default + + The "TaggedQueuing:Default" or "TQ:Default" option permits Tagged Queuing + based on the firmware version of the BusLogic Host Adapter and based on + whether the Queue Depth allows queuing multiple commands. + + TaggedQueuing:Enable + + The "TaggedQueuing:Enable" or "TQ:Enable" option enables Tagged Queuing for + all Target Devices on this Host Adapter, overriding any limitation that + would otherwise be imposed based on the Host Adapter firmware version. + + TaggedQueuing:Disable + + The "TaggedQueuing:Disable" or "TQ:Disable" option disables Tagged Queuing + for all Target Devices on this Host Adapter. + + TaggedQueuing: + + The "TaggedQueuing:" or "TQ:" option controls + Tagged Queuing individually for each Target Device. is a + sequence of "Y", "N", and "X" characters. "Y" enables Tagged Queuing, "N" + disables Tagged Queuing, and "X" accepts the default based on the firmware + version. The first character refers to Target Device 0, the second to + Target Device 1, and so on; if the sequence of "Y", "N", and "X" characters + does not cover all the Target Devices, unspecified characters are assumed + to be "X". + + The BusLogic Driver Error Recovery Option allows for explicitly specifying + the Error Recovery action to be performed when BusLogic_ResetCommand is + called due to a SCSI Command failing to complete successfully. The following + options are available: + + ErrorRecovery:Default + + The "ErrorRecovery:Default" or "ER:Default" option selects between the Hard + Reset and Bus Device Reset options based on the recommendation of the SCSI + Subsystem. + + ErrorRecovery:HardReset + + The "ErrorRecovery:HardReset" or "ER:HardReset" option will initiate a Host + Adapter Hard Reset which also causes a SCSI Bus Reset. + + ErrorRecovery:BusDeviceReset + + The "ErrorRecovery:BusDeviceReset" or "ER:BusDeviceReset" option will send + a Bus Device Reset message to the individual Target Device causing the + error. If Error Recovery is again initiated for this Target Device and no + SCSI Command to this Target Device has completed successfully since the Bus + Device Reset message was sent, then a Hard Reset will be attempted. + + ErrorRecovery:None + + The "ErrorRecovery:None" or "ER:None" option suppresses Error Recovery. + This option should only be selected if a SCSI Bus Reset or Bus Device Reset + will cause the Target Device or a critical operation to suffer a complete + and unrecoverable failure. + + ErrorRecovery: + + The "ErrorRecovery:" or "ER:" option controls + Error Recovery individually for each Target Device. is a + sequence of "D", "H", "B", and "N" characters. "D" selects Default, "H" + selects Hard Reset, "B" selects Bus Device Reset, and "N" selects None. + The first character refers to Target Device 0, the second to Target Device + 1, and so on; if the sequence of "D", "H", "B", and "N" characters does not + cover all the possible Target Devices, unspecified characters are assumed + to be "D". + + The BusLogic Driver Miscellaneous Options comprise the following: + + BusSettleTime: + + The "BusSettleTime:" or "BST:" option specifies the Bus Settle Time in + seconds. The Bus Settle Time is the amount of time to wait between a Host + Adapter Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI + Commands. If unspecified, it defaults to BusLogic_DefaultBusSettleTime. + + InhibitTargetInquiry + + The "InhibitTargetInquiry" option inhibits the execution of an Inquire + Target Devices or Inquire Installed Devices command on MultiMaster Host + Adapters. This may be necessary with some older Target Devices that do not + respond correctly when Logical Units above 0 are addressed. + + The BusLogic Driver Debugging Options comprise the following: + + TraceProbe + + The "TraceProbe" option enables tracing of Host Adapter Probing. + + TraceHardwareReset + + The "TraceHardwareReset" option enables tracing of Host Adapter Hardware + Reset. + + TraceConfiguration + + The "TraceConfiguration" option enables tracing of Host Adapter + Configuration. + + TraceErrors + + The "TraceErrors" option enables tracing of SCSI Commands that return an + error from the Target Device. The CDB and Sense Data will be printed for + each SCSI Command that fails. + + Debug + + The "Debug" option enables all debugging options. + + The following examples demonstrate setting the Queue Depth for Target Devices + 1 and 2 on the first host adapter to 7 and 15, the Queue Depth for all Target + Devices on the second host adapter to 31, and the Bus Settle Time on the + second host adapter to 30 seconds. + + Linux Kernel Command Line: + + linux BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30 + + LILO Linux Boot Loader (in /etc/lilo.conf): + + append = "BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30" + + INSMOD Loadable Kernel Module Installation Facility: + + insmod BusLogic.o \ + 'BusLogic_Options="QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30"' + + NOTE: Module Utilities 2.1.71 or later is required for correct parsing + of driver options containing commas. + +*/ + +static void BusLogic_ParseDriverOptions(char *OptionsString) +{ + while (true) + { + BusLogic_DriverOptions_T *DriverOptions = + &BusLogic_DriverOptions[BusLogic_DriverOptionsCount++]; + int TargetID; + memset(DriverOptions, 0, sizeof(BusLogic_DriverOptions_T)); + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_Default; + while (*OptionsString != '\0' && *OptionsString != ';') + { + /* Probing Options. */ + if (BusLogic_ParseKeyword(&OptionsString, "IO:")) + { + BusLogic_IO_Address_T IO_Address = + simple_strtoul(OptionsString, &OptionsString, 0); + BusLogic_ProbeOptions.LimitedProbeISA = true; + switch (IO_Address) + { + case 0x330: + BusLogic_ProbeOptions.Probe330 = true; + break; + case 0x334: + BusLogic_ProbeOptions.Probe334 = true; + break; + case 0x230: + BusLogic_ProbeOptions.Probe230 = true; + break; + case 0x234: + BusLogic_ProbeOptions.Probe234 = true; + break; + case 0x130: + BusLogic_ProbeOptions.Probe130 = true; + break; + case 0x134: + BusLogic_ProbeOptions.Probe134 = true; + break; + default: + BusLogic_Error("BusLogic: Invalid Driver Options " + "(illegal I/O Address 0x%X)\n", + NULL, IO_Address); + return; + } + } + else if (BusLogic_ParseKeyword(&OptionsString, "NoProbeISA")) + BusLogic_ProbeOptions.NoProbeISA = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoProbePCI")) + BusLogic_ProbeOptions.NoProbePCI = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoProbe")) + BusLogic_ProbeOptions.NoProbe = true; + else if (BusLogic_ParseKeyword(&OptionsString, "NoSortPCI")) + BusLogic_ProbeOptions.NoSortPCI = true; + else if (BusLogic_ParseKeyword(&OptionsString, "MultiMasterFirst")) + BusLogic_ProbeOptions.MultiMasterFirst = true; + else if (BusLogic_ParseKeyword(&OptionsString, "FlashPointFirst")) + BusLogic_ProbeOptions.FlashPointFirst = true; + /* Tagged Queuing Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:[") || + BusLogic_ParseKeyword(&OptionsString, "QD:[")) + { + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + { + unsigned short QueueDepth = + simple_strtoul(OptionsString, &OptionsString, 0); + if (QueueDepth > BusLogic_MaxTaggedQueueDepth) + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(illegal Queue Depth %d)\n", + NULL, QueueDepth); + return; + } + DriverOptions->QueueDepth[TargetID] = QueueDepth; + if (*OptionsString == ',') + OptionsString++; + else if (*OptionsString == ']') + break; + else + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(',' or ']' expected at '%s')\n", + NULL, OptionsString); + return; + } + } + if (*OptionsString != ']') + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(']' expected at '%s')\n", + NULL, OptionsString); + return; + } + else OptionsString++; + } + else if (BusLogic_ParseKeyword(&OptionsString, "QueueDepth:") || + BusLogic_ParseKeyword(&OptionsString, "QD:")) + { + unsigned short QueueDepth = + simple_strtoul(OptionsString, &OptionsString, 0); + if (QueueDepth == 0 || QueueDepth > BusLogic_MaxTaggedQueueDepth) + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(illegal Queue Depth %d)\n", + NULL, QueueDepth); + return; + } + DriverOptions->CommonQueueDepth = QueueDepth; + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + DriverOptions->QueueDepth[TargetID] = QueueDepth; + } + else if (BusLogic_ParseKeyword(&OptionsString, "TaggedQueuing:") || + BusLogic_ParseKeyword(&OptionsString, "TQ:")) + { + if (BusLogic_ParseKeyword(&OptionsString, "Default")) + { + DriverOptions->TaggedQueuingPermitted = 0x0000; + DriverOptions->TaggedQueuingPermittedMask = 0x0000; + } + else if (BusLogic_ParseKeyword(&OptionsString, "Enable")) + { + DriverOptions->TaggedQueuingPermitted = 0xFFFF; + DriverOptions->TaggedQueuingPermittedMask = 0xFFFF; + } + else if (BusLogic_ParseKeyword(&OptionsString, "Disable")) + { + DriverOptions->TaggedQueuingPermitted = 0x0000; + DriverOptions->TaggedQueuingPermittedMask = 0xFFFF; + } + else + { + unsigned short TargetBit; + for (TargetID = 0, TargetBit = 1; + TargetID < BusLogic_MaxTargetDevices; + TargetID++, TargetBit <<= 1) + switch (*OptionsString++) + { + case 'Y': + DriverOptions->TaggedQueuingPermitted |= TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + break; + case 'N': + DriverOptions->TaggedQueuingPermitted &= ~TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + break; + case 'X': + break; + default: + OptionsString--; + TargetID = BusLogic_MaxTargetDevices; + break; + } + } + } + /* Error Recovery Option. */ + else if (BusLogic_ParseKeyword(&OptionsString, "ErrorRecovery:") || + BusLogic_ParseKeyword(&OptionsString, "ER:")) + { + if (BusLogic_ParseKeyword(&OptionsString, "Default")) + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_Default; + else if (BusLogic_ParseKeyword(&OptionsString, "HardReset")) + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_HardReset; + else if (BusLogic_ParseKeyword(&OptionsString, "BusDeviceReset")) + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_BusDeviceReset; + else if (BusLogic_ParseKeyword(&OptionsString, "None")) + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_None; + else + for (TargetID = 0; + TargetID < BusLogic_MaxTargetDevices; + TargetID++) + switch (*OptionsString++) + { + case 'D': + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_Default; + break; + case 'H': + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_HardReset; + break; + case 'B': + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_BusDeviceReset; + break; + case 'N': + DriverOptions->ErrorRecoveryStrategy[TargetID] = + BusLogic_ErrorRecovery_None; + break; + default: + OptionsString--; + TargetID = BusLogic_MaxTargetDevices; + break; + } + } + /* Miscellaneous Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "BusSettleTime:") || + BusLogic_ParseKeyword(&OptionsString, "BST:")) + { + unsigned short BusSettleTime = + simple_strtoul(OptionsString, &OptionsString, 0); + if (BusSettleTime > 5 * 60) + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(illegal Bus Settle Time %d)\n", + NULL, BusSettleTime); + return; + } + DriverOptions->BusSettleTime = BusSettleTime; + } + else if (BusLogic_ParseKeyword(&OptionsString, + "InhibitTargetInquiry")) + DriverOptions->LocalOptions.InhibitTargetInquiry = true; + /* Debugging Options. */ + else if (BusLogic_ParseKeyword(&OptionsString, "TraceProbe")) + BusLogic_GlobalOptions.TraceProbe = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceHardwareReset")) + BusLogic_GlobalOptions.TraceHardwareReset = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceConfiguration")) + BusLogic_GlobalOptions.TraceConfiguration = true; + else if (BusLogic_ParseKeyword(&OptionsString, "TraceErrors")) + BusLogic_GlobalOptions.TraceErrors = true; + else if (BusLogic_ParseKeyword(&OptionsString, "Debug")) + { + BusLogic_GlobalOptions.TraceProbe = true; + BusLogic_GlobalOptions.TraceHardwareReset = true; + BusLogic_GlobalOptions.TraceConfiguration = true; + BusLogic_GlobalOptions.TraceErrors = true; + } + if (*OptionsString == ',') + OptionsString++; + else if (*OptionsString != ';' && *OptionsString != '\0') + { + BusLogic_Error("BusLogic: Unexpected Driver Option '%s' " + "ignored\n", NULL, OptionsString); + *OptionsString = '\0'; + } + } + if (!(BusLogic_DriverOptionsCount == 0 || + BusLogic_ProbeInfoCount == 0 || + BusLogic_DriverOptionsCount == BusLogic_ProbeInfoCount)) + { + BusLogic_Error("BusLogic: Invalid Driver Options " + "(all or no I/O Addresses must be specified)\n", NULL); return; } - CommandLineEntry->TaggedQueueDepth = TaggedQueueDepth; + /* + Tagged Queuing is disabled when the Queue Depth is 1 since queuing + multiple commands is not possible. + */ + for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) + if (DriverOptions->QueueDepth[TargetID] == 1) + { + unsigned short TargetBit = 1 << TargetID; + DriverOptions->TaggedQueuingPermitted &= ~TargetBit; + DriverOptions->TaggedQueuingPermittedMask |= TargetBit; + } + if (*OptionsString == ';') OptionsString++; + if (*OptionsString == '\0') return; } - if (IntegerCount >= 3) - CommandLineEntry->BusSettleTime = Integers[3]; - if (IntegerCount >= 4) - CommandLineEntry->LocalOptions.All = Integers[4]; - if (IntegerCount >= 5) - BusLogic_GlobalOptions.All |= Integers[5]; - if (!(BusLogic_CommandLineEntryCount == 0 || - BusLogic_ProbeInfoCount == 0 || - BusLogic_CommandLineEntryCount == BusLogic_ProbeInfoCount)) +} + + +/* + BusLogic_Setup handles processing of Kernel Command Line Arguments. +*/ + +void BusLogic_Setup(char *CommandLineString, int *CommandLineIntegers) +{ + if (CommandLineIntegers[0] != 0) { - BusLogic_Error("BusLogic: Invalid Command Line Entry " - "(all or no I/O Addresses must be specified)\n", NULL); + BusLogic_Error("BusLogic: Obsolete Command Line Entry " + "Format Ignored\n", NULL); return; } - if (Strings == NULL) return; - while (*Strings != '\0') - if (strncmp(Strings, "TQ:", 3) == 0) - { - Strings += 3; - if (strncmp(Strings, "Default", 7) == 0) - Strings += 7; - else if (strncmp(Strings, "Enable", 6) == 0) - { - Strings += 6; - CommandLineEntry->TaggedQueuingPermitted = 0xFFFF; - CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; - } - else if (strncmp(Strings, "Disable", 7) == 0) - { - Strings += 7; - CommandLineEntry->TaggedQueuingPermitted = 0x0000; - CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF; - } - else - for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) - switch (*Strings++) - { - case 'Y': - CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID; - CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; - break; - case 'N': - CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID; - break; - case 'X': - break; - default: - Strings--; - TargetID = BusLogic_MaxTargetDevices; - break; - } - } - else if (strncmp(Strings, "ER:", 3) == 0) - { - Strings += 3; - if (strncmp(Strings, "Default", 7) == 0) - Strings += 7; - else if (strncmp(Strings, "HardReset", 9) == 0) - { - Strings += 9; - memset(CommandLineEntry->ErrorRecoveryStrategy, - BusLogic_ErrorRecovery_HardReset, - sizeof(CommandLineEntry->ErrorRecoveryStrategy)); - } - else if (strncmp(Strings, "BusDeviceReset", 14) == 0) - { - Strings += 14; - memset(CommandLineEntry->ErrorRecoveryStrategy, - BusLogic_ErrorRecovery_BusDeviceReset, - sizeof(CommandLineEntry->ErrorRecoveryStrategy)); - } - else if (strncmp(Strings, "None", 4) == 0) - { - Strings += 4; - memset(CommandLineEntry->ErrorRecoveryStrategy, - BusLogic_ErrorRecovery_None, - sizeof(CommandLineEntry->ErrorRecoveryStrategy)); - } - else - for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++) - switch (*Strings++) - { - case 'D': - CommandLineEntry->ErrorRecoveryStrategy[TargetID] = - BusLogic_ErrorRecovery_Default; - break; - case 'H': - CommandLineEntry->ErrorRecoveryStrategy[TargetID] = - BusLogic_ErrorRecovery_HardReset; - break; - case 'B': - CommandLineEntry->ErrorRecoveryStrategy[TargetID] = - BusLogic_ErrorRecovery_BusDeviceReset; - break; - case 'N': - CommandLineEntry->ErrorRecoveryStrategy[TargetID] = - BusLogic_ErrorRecovery_None; - break; - default: - Strings--; - TargetID = BusLogic_MaxTargetDevices; - break; - } - } - else if (strcmp(Strings, "NoProbe") == 0 || - strcmp(Strings, "noprobe") == 0) - { - Strings += 7; - BusLogic_ProbeOptions.Bits.NoProbe = true; - } - else if (strncmp(Strings, "NoProbeISA", 10) == 0) - { - Strings += 10; - BusLogic_ProbeOptions.Bits.NoProbeISA = true; - } - else if (strncmp(Strings, "NoProbePCI", 10) == 0) - { - Strings += 10; - BusLogic_ProbeOptions.Bits.NoProbePCI = true; - } - else if (strncmp(Strings, "NoSortPCI", 9) == 0) - { - Strings += 9; - BusLogic_ProbeOptions.Bits.NoSortPCI = true; - } - else if (strncmp(Strings, "MultiMasterFirst", 16) == 0) - { - Strings += 16; - BusLogic_ProbeOptions.Bits.ProbeMultiMasterFirst = true; - } - else if (strncmp(Strings, "FlashPointFirst", 15) == 0) - { - Strings += 15; - BusLogic_ProbeOptions.Bits.ProbeFlashPointFirst = true; - } - else if (strncmp(Strings, "Debug", 5) == 0) - { - Strings += 5; - BusLogic_GlobalOptions.Bits.TraceProbe = true; - BusLogic_GlobalOptions.Bits.TraceHardReset = true; - BusLogic_GlobalOptions.Bits.TraceConfiguration = true; - BusLogic_GlobalOptions.Bits.TraceErrors = true; - } - else if (*Strings == ',') - Strings++; - else - { - BusLogic_Error("BusLogic: Unexpected Command Line String '%s' " - "ignored\n", NULL, Strings); - break; - } + if (CommandLineString == NULL || *CommandLineString == '\0') return; + BusLogic_ParseDriverOptions(CommandLineString); } diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h --- v2.0.33/linux/drivers/scsi/BusLogic.h Wed Oct 15 15:24:02 1997 +++ linux/drivers/scsi/BusLogic.h Wed Jun 3 15:17:48 1998 @@ -2,12 +2,11 @@ Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters - Copyright 1995 by Leonard N. Zubkoff + Copyright 1995-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, provided that none of the source code or runtime - copyright notices are removed or modified. + 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 @@ -37,7 +36,9 @@ typedef kdev_t KernelDevice_T; typedef struct proc_dir_entry PROC_DirectoryEntry_T; +typedef unsigned long ProcessorFlags_T; typedef struct pt_regs Registers_T; +typedef struct partition PartitionTable_T; typedef Scsi_Host_Template SCSI_Host_Template_T; typedef struct Scsi_Host SCSI_Host_T; typedef struct scsi_device SCSI_Device_T; @@ -66,28 +67,19 @@ Define the BusLogic SCSI Host Template structure. */ -#define BUSLOGIC \ - { NULL, /* Next */ \ - NULL, /* Usage Count Pointer */ \ - &BusLogic_ProcDirectoryEntry, /* /proc Directory Entry */ \ - BusLogic_ProcDirectoryInfo, /* /proc Info Function */ \ - "BusLogic", /* Driver Name */ \ - BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ - BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ - BusLogic_DriverInfo, /* Driver Info Function */ \ - NULL, /* Command Function */ \ - BusLogic_QueueCommand, /* Queue Command Function */ \ - BusLogic_AbortCommand, /* Abort Command Function */ \ - BusLogic_ResetCommand, /* Reset Command Function */ \ - NULL, /* Slave Attach Function */ \ - BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ - 0, /* Can Queue */ \ - 0, /* This ID */ \ - 0, /* Scatter/Gather Table Size */ \ - 0, /* SCSI Commands per LUN */ \ - 0, /* Present */ \ - 1, /* Default Unchecked ISA DMA */ \ - ENABLE_CLUSTERING } /* Enable Clustering */ +#define BUSLOGIC \ + { proc_dir: &BusLogic_ProcDirectoryEntry, /* ProcFS Directory Entry */ \ + proc_info: BusLogic_ProcDirectoryInfo, /* ProcFS Info Function */ \ + name: "BusLogic", /* Driver Name */ \ + detect: BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \ + release: BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \ + info: BusLogic_DriverInfo, /* Driver Info Function */ \ + queuecommand: BusLogic_QueueCommand, /* Queue Command Function */ \ + abort: BusLogic_AbortCommand, /* Abort Command Function */ \ + reset: BusLogic_ResetCommand, /* Reset Command Function */ \ + bios_param: BusLogic_BIOSDiskParameters, /* BIOS Disk Parameters */ \ + unchecked_isa_dma: 1, /* Default Initial Value */ \ + use_clustering: ENABLE_CLUSTERING } /* Enable Clustering */ /* @@ -98,6 +90,24 @@ /* + FlashPoint support is only available for the Intel x86 Architecture with + CONFIG_PCI set. +*/ + +#ifndef __i386__ +#undef CONFIG_SCSI_OMIT_FLASHPOINT +#define CONFIG_SCSI_OMIT_FLASHPOINT +#endif + +#ifndef CONFIG_PCI +#undef CONFIG_SCSI_OMIT_FLASHPOINT +#define CONFIG_SCSI_OMIT_FLASHPOINT +#define BusLogic_InitializeProbeInfoListISA \ + BusLogic_InitializeProbeInfoList +#endif + + +/* Define the maximum number of BusLogic Host Adapters supported by this driver. */ @@ -121,16 +131,17 @@ /* - Define the maximum, preferred, and default Queue Depth to allow for Target - Devices depending on whether or not they support Tagged Queuing and whether - or not ISA Bounce Buffers are required. + Define the maximum, maximum automatic, minimum automatic, and default Queue + Depth to allow for Target Devices depending on whether or not they support + Tagged Queuing and whether or not ISA Bounce Buffers are required. */ -#define BusLogic_MaxTaggedQueueDepth 63 -#define BusLogic_PreferredTaggedQueueDepth 28 -#define BusLogic_TaggedQueueDepthBounceBuffers 2 -#define BusLogic_TaggedQueueDepthAutomatic 0 +#define BusLogic_MaxTaggedQueueDepth 64 +#define BusLogic_MaxAutomaticTaggedQueueDepth 28 +#define BusLogic_MinAutomaticTaggedQueueDepth 7 +#define BusLogic_TaggedQueueDepthBB 3 #define BusLogic_UntaggedQueueDepth 3 +#define BusLogic_UntaggedQueueDepthBB 2 /* @@ -144,11 +155,29 @@ /* + Define the maximum number of Mailboxes that should be used for MultiMaster + Host Adapters. This number is chosen to be larger than the maximum Host + Adapter Queue Depth and small enough so that the Host Adapter structure + does not cross an allocation block size boundary. +*/ + +#define BusLogic_MaxMailboxes 211 + + +/* + Define the number of CCBs that should be allocated as a group to optimize + Kernel memory allocation. +*/ + +#define BusLogic_CCB_AllocationGroupSize 7 + + +/* Define the Host Adapter Line and Message Buffer Sizes. */ #define BusLogic_LineBufferSize 100 -#define BusLogic_MessageBufferSize 9900 +#define BusLogic_MessageBufferSize 9700 /* @@ -167,7 +196,27 @@ static char *BusLogic_MessageLevelMap[] = - { KERN_INFO, KERN_INFO, KERN_NOTICE, KERN_WARNING, KERN_ERR }; + { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING, KERN_ERR }; + + +/* + Define Driver Message macros. +*/ + +#define BusLogic_Announce(Format, Arguments...) \ + BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments) + +#define BusLogic_Info(Format, Arguments...) \ + BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments) + +#define BusLogic_Notice(Format, Arguments...) \ + BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments) + +#define BusLogic_Warning(Format, Arguments...) \ + BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments) + +#define BusLogic_Error(Format, Arguments...) \ + BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments) /* @@ -187,11 +236,34 @@ #define BusLogic_FlashPointAddressCount 256 static int - BusLogic_HostAdapter_AddressCount[3] = + BusLogic_HostAdapterAddressCount[3] = { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount }; /* + Define macros for testing the Host Adapter Type. +*/ + +#ifndef CONFIG_SCSI_OMIT_FLASHPOINT + +#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \ + (HostAdapter->HostAdapterType == BusLogic_MultiMaster) + +#define BusLogic_FlashPointHostAdapterP(HostAdapter) \ + (HostAdapter->HostAdapterType == BusLogic_FlashPoint) + +#else + +#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \ + (true) + +#define BusLogic_FlashPointHostAdapterP(HostAdapter) \ + (false) + +#endif + + +/* Define the possible Host Adapter Bus Types. */ @@ -204,6 +276,7 @@ BusLogic_VESA_Bus = 4, BusLogic_MCA_Bus = 5 } +__attribute__ ((packed)) BusLogic_HostAdapterBusType_T; static char @@ -257,6 +330,13 @@ /* + Define a 32 bit Base Address data type. +*/ + +typedef unsigned int BusLogic_Base_Address_T; + + +/* Define a 32 bit Bus Address data type. */ @@ -288,11 +368,10 @@ typedef struct BusLogic_ProbeInfo { + BusLogic_HostAdapterType_T HostAdapterType; + BusLogic_HostAdapterBusType_T HostAdapterBusType; BusLogic_IO_Address_T IO_Address; BusLogic_PCI_Address_T PCI_Address; - BusLogic_HostAdapterType_T HostAdapterType:2; - BusLogic_HostAdapterBusType_T HostAdapterBusType:3; - unsigned char :3; unsigned char Bus; unsigned char Device; unsigned char IRQ_Channel; @@ -301,35 +380,24 @@ /* - BusLogic_ISA_StandardAddresses is the list of standard ISA I/O Addresses at - which BusLogic MultiMaster Host Adapters may potentially be found. The first - I/O Address 0x330 is known as the "Primary" I/O Address. A Host Adapter - configured to use the Primary I/O Address will always be the preferred boot - device. -*/ - -#define BusLogic_ISA_StandardAddressesCount 6 - -static BusLogic_IO_Address_T - BusLogic_ISA_StandardAddresses[BusLogic_ISA_StandardAddressesCount] = - { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134 }; - - -/* Define the Probe Options. */ -typedef union BusLogic_ProbeOptions +typedef struct BusLogic_ProbeOptions { - unsigned short All; - struct { - boolean NoProbe:1; /* Bit 0 */ - boolean NoProbeISA:1; /* Bit 1 */ - boolean NoProbePCI:1; /* Bit 2 */ - boolean NoSortPCI:1; /* Bit 3 */ - boolean ProbeMultiMasterFirst:1; /* Bit 4 */ - boolean ProbeFlashPointFirst:1; /* Bit 5 */ - } Bits; + boolean NoProbe:1; /* Bit 0 */ + boolean NoProbeISA:1; /* Bit 1 */ + boolean NoProbePCI:1; /* Bit 2 */ + boolean NoSortPCI:1; /* Bit 3 */ + boolean MultiMasterFirst:1; /* Bit 4 */ + boolean FlashPointFirst:1; /* Bit 5 */ + boolean LimitedProbeISA:1; /* Bit 6 */ + boolean Probe330:1; /* Bit 7 */ + boolean Probe334:1; /* Bit 8 */ + boolean Probe230:1; /* Bit 9 */ + boolean Probe234:1; /* Bit 10 */ + boolean Probe130:1; /* Bit 11 */ + boolean Probe134:1; /* Bit 12 */ } BusLogic_ProbeOptions_T; @@ -338,15 +406,12 @@ Define the Global Options. */ -typedef union BusLogic_GlobalOptions +typedef struct BusLogic_GlobalOptions { - unsigned short All; - struct { - boolean TraceProbe:1; /* Bit 0 */ - boolean TraceHardReset:1; /* Bit 1 */ - boolean TraceConfiguration:1; /* Bit 2 */ - boolean TraceErrors:1; /* Bit 3 */ - } Bits; + boolean TraceProbe:1; /* Bit 0 */ + boolean TraceHardwareReset:1; /* Bit 1 */ + boolean TraceConfiguration:1; /* Bit 2 */ + boolean TraceErrors:1; /* Bit 3 */ } BusLogic_GlobalOptions_T; @@ -355,13 +420,9 @@ Define the Local Options. */ -typedef union BusLogic_LocalOptions +typedef struct BusLogic_LocalOptions { - unsigned short All; - struct { - boolean InhibitTargetInquiry:1; /* Bit 0 */ - boolean InhibitInterruptTest:1; /* Bit 1 */ - } Bits; + boolean InhibitTargetInquiry:1; /* Bit 0 */ } BusLogic_LocalOptions_T; @@ -619,10 +680,13 @@ unsigned char Signature; /* Byte 17 */ unsigned char CharacterD; /* Byte 18 */ unsigned char HostBusType; /* Byte 19 */ - unsigned char :8; /* Byte 20 */ - unsigned char :8; /* Byte 21 */ + unsigned char WideTransfersPermittedID0to7; /* Byte 20 */ + unsigned char WideTransfersActiveID0to7; /* Byte 21 */ BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */ unsigned char DisconnectPermittedID8to15; /* Byte 30 */ + unsigned char :8; /* Byte 31 */ + unsigned char WideTransfersPermittedID8to15; /* Byte 32 */ + unsigned char WideTransfersActiveID8to15; /* Byte 33 */ } BusLogic_SetupInformation_T; @@ -903,16 +967,6 @@ /* - Define the Lock data structure. Until a true symmetric multiprocessing - kernel with fine grained locking is available, acquiring the lock is - implemented as saving the processor flags and disabling interrupts, and - releasing the lock restores the saved processor flags. -*/ - -typedef unsigned long BusLogic_Lock_T; - - -/* Define the Outgoing Mailbox Action Codes. */ @@ -1064,6 +1118,21 @@ /* + Define the Driver CCB Status Codes. +*/ + +typedef enum +{ + BusLogic_CCB_Free = 0, + BusLogic_CCB_Active = 1, + BusLogic_CCB_Completed = 2, + BusLogic_CCB_Reset = 3 +} +__attribute__ ((packed)) +BusLogic_CCB_Status_T; + + +/* Define the 32 Bit Mode Command Control Block (CCB) structure. The first 40 bytes are defined by and common to both the MultiMaster Firmware and the FlashPoint SCCB Manager. The next 60 bytes are defined by the FlashPoint @@ -1113,27 +1182,26 @@ FlashPoint SCCB Manager Defined Portion. */ void (*CallbackFunction)(struct BusLogic_CCB *); /* Bytes 40-43 */ - BusLogic_IO_Address_T BaseAddress; /* Bytes 44-47 */ + BusLogic_Base_Address_T BaseAddress; /* Bytes 44-47 */ BusLogic_CompletionCode_T CompletionCode; /* Byte 48 */ +#ifndef CONFIG_SCSI_OMIT_FLASHPOINT unsigned char :8; /* Byte 49 */ unsigned short OS_Flags; /* Bytes 50-51 */ unsigned char Private[48]; /* Bytes 52-99 */ +#endif /* BusLogic Linux Driver Defined Portion. */ - struct BusLogic_HostAdapter *HostAdapter; - SCSI_Command_T *Command; - enum { BusLogic_CCB_Free = 0, - BusLogic_CCB_Active = 1, - BusLogic_CCB_Completed = 2, - BusLogic_CCB_Reset = 3 } Status; + boolean AllocationGroupHead; + BusLogic_CCB_Status_T Status; unsigned long SerialNumber; + SCSI_Command_T *Command; + struct BusLogic_HostAdapter *HostAdapter; struct BusLogic_CCB *Next; struct BusLogic_CCB *NextAll; BusLogic_ScatterGatherSegment_T ScatterGatherList[BusLogic_ScatterGatherLimit]; } -__attribute__ ((packed)) BusLogic_CCB_T; @@ -1166,32 +1234,49 @@ /* - Define the Linux BusLogic Driver Command Line Entry structure. + Define the BusLogic Driver Options structure. */ -typedef struct BusLogic_CommandLineEntry +typedef struct BusLogic_DriverOptions { - BusLogic_IO_Address_T IO_Address; - unsigned short TaggedQueueDepth; - unsigned short BusSettleTime; unsigned short TaggedQueuingPermitted; unsigned short TaggedQueuingPermittedMask; + unsigned short BusSettleTime; BusLogic_LocalOptions_T LocalOptions; + unsigned char CommonQueueDepth; + unsigned char QueueDepth[BusLogic_MaxTargetDevices]; BusLogic_ErrorRecoveryStrategy_T ErrorRecoveryStrategy[BusLogic_MaxTargetDevices]; } -BusLogic_CommandLineEntry_T; +BusLogic_DriverOptions_T; + + +/* + Define the Host Adapter Target Flags structure. +*/ + +typedef struct BusLogic_TargetFlags +{ + boolean TargetExists:1; + boolean TaggedQueuingSupported:1; + boolean WideTransfersSupported:1; + boolean TaggedQueuingActive:1; + boolean WideTransfersActive:1; + boolean CommandSuccessfulFlag:1; + boolean TargetInfoReported:1; +} +BusLogic_TargetFlags_T; /* - Define the Host Adapter Target Device Statistics structure. + Define the Host Adapter Target Statistics structure. */ #define BusLogic_SizeBuckets 10 typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets]; -typedef struct BusLogic_TargetDeviceStatistics +typedef struct BusLogic_TargetStatistics { unsigned int CommandsAttempted; unsigned int CommandsCompleted; @@ -1211,7 +1296,7 @@ unsigned short HostAdapterResetsAttempted; unsigned short HostAdapterResetsCompleted; } -BusLogic_TargetDeviceStatistics_T; +BusLogic_TargetStatistics_T; /* @@ -1230,7 +1315,7 @@ typedef struct FlashPoint_Info { - BusLogic_IO_Address_T BaseAddress; /* Bytes 0-3 */ + BusLogic_Base_Address_T BaseAddress; /* Bytes 0-3 */ boolean Present; /* Byte 4 */ unsigned char IRQ_Channel; /* Byte 5 */ unsigned char SCSI_ID; /* Byte 6 */ @@ -1265,12 +1350,14 @@ /* - Define the Linux BusLogic Driver Host Adapter structure. + Define the BusLogic Driver Host Adapter structure. */ typedef struct BusLogic_HostAdapter { SCSI_Host_T *SCSI_Host; + BusLogic_HostAdapterType_T HostAdapterType; + BusLogic_HostAdapterBusType_T HostAdapterBusType; BusLogic_IO_Address_T IO_Address; BusLogic_PCI_Address_T PCI_Address; unsigned short AddressCount; @@ -1278,19 +1365,16 @@ unsigned char ModelName[9]; unsigned char FirmwareVersion[6]; unsigned char FullModelName[18]; - unsigned char InterruptLabel[68]; + unsigned char Bus; + unsigned char Device; unsigned char IRQ_Channel; unsigned char DMA_Channel; unsigned char SCSI_ID; - unsigned char Bus; - unsigned char Device; - BusLogic_HostAdapterType_T HostAdapterType; - BusLogic_HostAdapterBusType_T HostAdapterBusType:3; boolean IRQ_ChannelAcquired:1; boolean DMA_ChannelAcquired:1; boolean ExtendedTranslationEnabled:1; boolean ParityCheckingEnabled:1; - boolean BusResetEnabled; + boolean BusResetEnabled:1; boolean LevelSensitiveInterrupt:1; boolean HostWideSCSI:1; boolean HostDifferentialSCSI:1; @@ -1304,9 +1388,11 @@ boolean StrictRoundRobinModeSupport:1; boolean SCAM_Enabled:1; boolean SCAM_Level2:1; - boolean HostAdapterInitialized; - boolean HostAdapterResetRequested:1; - volatile boolean HostAdapterCommandCompleted:1; + boolean HostAdapterInitialized:1; + boolean HostAdapterExternalReset:1; + boolean HostAdapterInternalError:1; + boolean ProcessCompletedCCBsActive; + volatile boolean HostAdapterCommandCompleted; unsigned short HostAdapterScatterGatherLimit; unsigned short DriverScatterGatherLimit; unsigned short MaxTargetDevices; @@ -1317,8 +1403,8 @@ unsigned short AllocatedCCBs; unsigned short DriverQueueDepth; unsigned short HostAdapterQueueDepth; - unsigned short TaggedQueueDepth; unsigned short UntaggedQueueDepth; + unsigned short CommonQueueDepth; unsigned short BusSettleTime; unsigned short SynchronousPermitted; unsigned short FastPermitted; @@ -1327,26 +1413,25 @@ unsigned short DisconnectPermitted; unsigned short TaggedQueuingPermitted; unsigned short ExternalHostAdapterResets; - BusLogic_LocalOptions_T LocalOptions; + unsigned short HostAdapterInternalErrors; + unsigned short TargetDeviceCount; + unsigned short MessageBufferLength; BusLogic_BusAddress_T BIOS_Address; - BusLogic_InstalledDevices_T InstalledDevices; - BusLogic_SynchronousValues_T SynchronousValues; - BusLogic_SynchronousPeriod_T SynchronousPeriod; - BusLogic_CommandLineEntry_T *CommandLineEntry; - FlashPoint_Info_T *FlashPointInfo; + BusLogic_DriverOptions_T *DriverOptions; + FlashPoint_Info_T FlashPointInfo; FlashPoint_CardHandle_T CardHandle; struct BusLogic_HostAdapter *Next; - char *MessageBuffer; - int MessageBufferLength; BusLogic_CCB_T *All_CCBs; BusLogic_CCB_T *Free_CCBs; + BusLogic_CCB_T *FirstCompletedCCB; + BusLogic_CCB_T *LastCompletedCCB; BusLogic_CCB_T *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices]; BusLogic_ErrorRecoveryStrategy_T ErrorRecoveryStrategy[BusLogic_MaxTargetDevices]; - boolean TaggedQueuingSupported[BusLogic_MaxTargetDevices]; - boolean TaggedQueuingActive[BusLogic_MaxTargetDevices]; - boolean CommandSuccessfulFlag[BusLogic_MaxTargetDevices]; + BusLogic_TargetFlags_T TargetFlags[BusLogic_MaxTargetDevices]; unsigned char QueueDepth[BusLogic_MaxTargetDevices]; + unsigned char SynchronousPeriod[BusLogic_MaxTargetDevices]; + unsigned char SynchronousOffset[BusLogic_MaxTargetDevices]; unsigned char ActiveCommands[BusLogic_MaxTargetDevices]; unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices]; unsigned long LastSequencePoint[BusLogic_MaxTargetDevices]; @@ -1358,7 +1443,11 @@ BusLogic_IncomingMailbox_T *FirstIncomingMailbox; BusLogic_IncomingMailbox_T *LastIncomingMailbox; BusLogic_IncomingMailbox_T *NextIncomingMailbox; - BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics; + BusLogic_TargetStatistics_T TargetStatistics[BusLogic_MaxTargetDevices]; + unsigned char MailboxSpace[BusLogic_MaxMailboxes + * (sizeof(BusLogic_OutgoingMailbox_T) + + sizeof(BusLogic_IncomingMailbox_T))]; + char MessageBuffer[BusLogic_MessageBufferSize]; } BusLogic_HostAdapter_T; @@ -1377,14 +1466,49 @@ /* + Define a structure for the SCSI Inquiry command results. +*/ + +typedef struct SCSI_Inquiry +{ + unsigned char PeripheralDeviceType:5; /* Byte 0 Bits 0-4 */ + unsigned char PeripheralQualifier:3; /* Byte 0 Bits 5-7 */ + unsigned char DeviceTypeModifier:7; /* Byte 1 Bits 0-6 */ + boolean RMB:1; /* Byte 1 Bit 7 */ + unsigned char ANSI_ApprovedVersion:3; /* Byte 2 Bits 0-2 */ + unsigned char ECMA_Version:3; /* Byte 2 Bits 3-5 */ + unsigned char ISO_Version:2; /* Byte 2 Bits 6-7 */ + unsigned char ResponseDataFormat:4; /* Byte 3 Bits 0-3 */ + unsigned char :2; /* Byte 3 Bits 4-5 */ + boolean TrmIOP:1; /* Byte 3 Bit 6 */ + boolean AENC:1; /* Byte 3 Bit 7 */ + unsigned char AdditionalLength; /* Byte 4 */ + unsigned char :8; /* Byte 5 */ + unsigned char :8; /* Byte 6 */ + boolean SftRe:1; /* Byte 7 Bit 0 */ + boolean CmdQue:1; /* Byte 7 Bit 1 */ + boolean :1; /* Byte 7 Bit 2 */ + boolean Linked:1; /* Byte 7 Bit 3 */ + boolean Sync:1; /* Byte 7 Bit 4 */ + boolean WBus16:1; /* Byte 7 Bit 5 */ + boolean WBus32:1; /* Byte 7 Bit 6 */ + boolean RelAdr:1; /* Byte 7 Bit 7 */ + unsigned char VendorIdentification[8]; /* Bytes 8-15 */ + unsigned char ProductIdentification[16]; /* Bytes 16-31 */ + unsigned char ProductRevisionLevel[4]; /* Bytes 32-35 */ +} +SCSI_Inquiry_T; + + +/* BusLogic_AcquireHostAdapterLock acquires exclusive access to Host Adapter. */ static inline void BusLogic_AcquireHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter, - BusLogic_Lock_T *Lock) + ProcessorFlags_T *ProcessorFlags) { - save_flags(*Lock); + save_flags(*ProcessorFlags); cli(); } @@ -1395,32 +1519,32 @@ static inline void BusLogic_ReleaseHostAdapterLock(BusLogic_HostAdapter_T *HostAdapter, - BusLogic_Lock_T *Lock) + ProcessorFlags_T *ProcessorFlags) { - restore_flags(*Lock); + restore_flags(*ProcessorFlags); } /* - BusLogic_AcquireHostAdapterLockID acquires exclusive access to Host Adapter, - but is only called when interrupts are disabled. + BusLogic_AcquireHostAdapterLockIH acquires exclusive access to Host Adapter, + but is only called from the interrupt handler when interrupts are disabled. */ static inline -void BusLogic_AcquireHostAdapterLockID(BusLogic_HostAdapter_T *HostAdapter, - BusLogic_Lock_T *Lock) +void BusLogic_AcquireHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter, + ProcessorFlags_T *ProcessorFlags) { } /* - BusLogic_ReleaseHostAdapterLockID releases exclusive access to Host Adapter, - but is only called when interrupts are disabled. + BusLogic_ReleaseHostAdapterLockIH releases exclusive access to Host Adapter, + but is only called from the interrupt handler when interrupts are disabled. */ static inline -void BusLogic_ReleaseHostAdapterLockID(BusLogic_HostAdapter_T *HostAdapter, - BusLogic_Lock_T *Lock) +void BusLogic_ReleaseHostAdapterLockIH(BusLogic_HostAdapter_T *HostAdapter, + ProcessorFlags_T *ProcessorFlags) { } @@ -1526,10 +1650,11 @@ static inline void BusLogic_Delay(int Seconds) { + int Milliseconds = 1000 * Seconds; unsigned long ProcessorFlags; save_flags(ProcessorFlags); sti(); - while (--Seconds >= 0) udelay(1000000); + while (--Milliseconds >= 0) udelay(1000); restore_flags(ProcessorFlags); } @@ -1551,6 +1676,19 @@ /* + Virtual_to_32Bit_Virtual maps between Kernel Virtual Addresses and + 32 bit Kernel Virtual Addresses. This avoids compilation warnings + on 64 bit architectures. +*/ + +static inline +BusLogic_BusAddress_T Virtual_to_32Bit_Virtual(void *VirtualAddress) +{ + return (BusLogic_BusAddress_T) (unsigned long) VirtualAddress; +} + + +/* BusLogic_IncrementErrorCounter increments Error Counter by 1, stopping at 65535 rather than wrapping around to 0. */ @@ -1588,93 +1726,27 @@ { int Index = 0; if (Amount < 8*1024) - if (Amount < 2*1024) - Index = (Amount < 1*1024 ? 0 : 1); - else Index = (Amount < 4*1024 ? 2 : 3); + { + if (Amount < 2*1024) + Index = (Amount < 1*1024 ? 0 : 1); + else Index = (Amount < 4*1024 ? 2 : 3); + } else if (Amount < 128*1024) - if (Amount < 32*1024) - Index = (Amount < 16*1024 ? 4 : 5); - else Index = (Amount < 64*1024 ? 6 : 7); + { + if (Amount < 32*1024) + Index = (Amount < 16*1024 ? 4 : 5); + else Index = (Amount < 64*1024 ? 6 : 7); + } else Index = (Amount < 256*1024 ? 8 : 9); CommandSizeBuckets[Index]++; } /* - If CONFIG_PCI is not set, force CONFIG_SCSI_OMIT_FLASHPOINT, and use the - ISA only probe function as the general one. -*/ - -#ifndef CONFIG_PCI - -#undef CONFIG_SCSI_OMIT_FLASHPOINT -#define CONFIG_SCSI_OMIT_FLASHPOINT - -#define BusLogic_InitializeProbeInfoListISA BusLogic_InitializeProbeInfoList - -#endif - - -/* - FlashPoint support is only available for the Intel x86 Architecture. -*/ - -#ifndef __i386__ - -#undef CONFIG_SCSI_OMIT_FLASHPOINT -#define CONFIG_SCSI_OMIT_FLASHPOINT - -#endif - - -/* - Define macros for testing the Host Adapter Type. -*/ - -#ifndef CONFIG_SCSI_OMIT_FLASHPOINT - -#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \ - (HostAdapter->HostAdapterType == BusLogic_MultiMaster) - -#define BusLogic_FlashPointHostAdapterP(HostAdapter) \ - (HostAdapter->HostAdapterType == BusLogic_FlashPoint) - -#else - -#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \ - (true) - -#define BusLogic_FlashPointHostAdapterP(HostAdapter) \ - (false) - -#endif - - -/* - Define Driver Message Macros. -*/ - -#define BusLogic_Announce(Format, Arguments...) \ - BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments) - -#define BusLogic_Info(Format, Arguments...) \ - BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments) - -#define BusLogic_Notice(Format, Arguments...) \ - BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments) - -#define BusLogic_Warning(Format, Arguments...) \ - BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments) - -#define BusLogic_Error(Format, Arguments...) \ - BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments) - - -/* Define the version number of the FlashPoint Firmware (SCCB Manager). */ -#define FlashPoint_FirmwareVersion "5.01" +#define FlashPoint_FirmwareVersion "5.02" /* @@ -1682,35 +1754,22 @@ */ #define FlashPoint_NormalInterrupt 0x00 +#define FlashPoint_InternalError 0xFE #define FlashPoint_ExternalBusReset 0xFF /* - Define prototypes for the FlashPoint SCCB Manager Functions. -*/ - -extern unsigned char FlashPoint_ProbeHostAdapter(FlashPoint_Info_T *); -extern FlashPoint_CardHandle_T - FlashPoint_HardResetHostAdapter(FlashPoint_Info_T *); -extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *); -extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *); -extern boolean FlashPoint_InterruptPending(FlashPoint_CardHandle_T); -extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T); -extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T); - - -/* Define prototypes for the forward referenced BusLogic Driver Internal Functions. */ -static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB); +static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *); static void BusLogic_InterruptHandler(int, void *, Registers_T *); static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *, - SCSI_Command_T *, - unsigned int); -static void BusLogic_Message(BusLogic_MessageLevel_T, char *Format, + SCSI_Command_T *, unsigned int); +static void BusLogic_Message(BusLogic_MessageLevel_T, char *, BusLogic_HostAdapter_T *, ...); +static void BusLogic_ParseDriverOptions(char *); #endif /* BusLogic_DriverVersion */ diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/ChangeLog.ncr53c8xx linux/drivers/scsi/ChangeLog.ncr53c8xx --- v2.0.33/linux/drivers/scsi/ChangeLog.ncr53c8xx Thu Aug 14 10:30:08 1997 +++ linux/drivers/scsi/ChangeLog.ncr53c8xx Wed Jun 3 15:17:48 1998 @@ -1,3 +1,75 @@ +Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr) + * Revision 2.5f + - Use FAST-5 instead of SLOW for slow scsi devices according to + new SPI-2 draft. + - Make some changes in order to accomodate with 875 rev <= 3 + device errata listing 397. Minor consequences are: + . Leave use of PCI Write and Invalidate under user control. + Now, by default the driver does not enable PCI MWI and option + 'specf:y' is required in order to enable this feature. + . Memory Read Line is not enabled for 875 and 875-like chips. + . Programmed burst length set to 64 DWORDS (instead of 128). + (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS) + +Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5e + - Add 'buschk' boot option. + This option enables checking of SCSI BUS data lines after SCSI + RESET (set by default). (Submitted by Richard Waltham). + - Update the README file. + +Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5d + - Dispatch CONDITION MET and RESERVATION CONFLICT scsi status + as OK driver status. + - Update the README file and the Symbios NVRAM format definition + with removable media flags values (available with SDMS 4.09). + +Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5c + - Several PCI configuration registers fix-ups for powerpc. + (Patch sent by Cort). + +Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5b + - Add 'ncr53c8xx' char pointer variable. This variable allows to + pass a boot command to the driver when it is loaded as a module. + Option separator is ' ' instead of ','. Example: + insmod /ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n' + - Always use 'driver_setup.settle_delay' for internal resets. + 2 seconds hardcoded is sometimes too short. Suggested by Richard W. + This delay may be shortenned in order to avoid spurious timeouts. + - Fix release module stuff that failed for more than 1 controller. + - For linux versions > 1.3.70, trust the 'dev_id' parameter passed + to the interrupt handler (dev_id = struct ncb *). + - Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts. + Suggested by Stefan Esser. + +Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5a + - Update Configure.help for inclusion in linux-2.1.51/2/3 + - Use BASE_2 address from PCI config space instead of some + IO register for getting the on-board SRAM bus address. + - Remove error testing of pcibios_read/write functions. + These functions are intended to be used for successfully + detected PCI devices. Expecting error condition from them + is nothing but paranoia. + +Thu Aug 21 23:00 1997 Gerard Roudier (groudier@club-internet.fr) + * revision 2.5 + - 53C860 chip support fix. + - Move the 'host_status' to the last DWORD of the CCB header. + This header is copied back by the script processor. This + guarantees that the header is entirely copied back over + the PCI when the CPU completes a CCB. + - (re)read ISTAT prior to scanning CCBs for completion. This + ensure that any posted buffer are flushed prior CCBs scan. + - Support for BIG ENDIAN cpu. Added by Cort . + Initial patch did'nt support disconnections and tagged commands. + I've completed the patch and it seems that all is ok now. + Only some powerpc under 2.1.X is supported for the moment. + - Misc. trivial fixes and cleanups. + Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr) * revision 2.4 Several clean-ups: diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in --- v2.0.33/linux/drivers/scsi/Config.in Mon Sep 15 09:41:28 1997 +++ linux/drivers/scsi/Config.in Wed Jun 3 15:17:48 1998 @@ -20,14 +20,12 @@ dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then - bool ' Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y bool ' Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then - int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8 + int ' Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24 fi - bool ' Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N - int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15 + int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5 fi dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI @@ -79,6 +77,9 @@ fi fi dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI +if [ "$CONFIG_SCSI_PPA" != "n" ]; then + bool ' Buggy EPP chipset support' CONFIG_SCSI_PPA_HAVE_PEDANTIC +fi dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI if [ "$CONFIG_PCI" = "y" ]; then diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c --- v2.0.33/linux/drivers/scsi/FlashPoint.c Mon Aug 11 00:10:00 1997 +++ linux/drivers/scsi/FlashPoint.c Wed Jun 3 15:17:48 1998 @@ -19,40 +19,15 @@ #include -/* - If CONFIG_PCI is not set, force CONFIG_SCSI_OMIT_FLASHPOINT. -*/ - -#ifndef CONFIG_PCI - -#undef CONFIG_SCSI_OMIT_FLASHPOINT -#define CONFIG_SCSI_OMIT_FLASHPOINT - -#endif - - -/* - FlashPoint support is only available for the Intel x86 Architecture. -*/ - -#ifndef __i386__ - -#undef CONFIG_SCSI_OMIT_FLASHPOINT -#define CONFIG_SCSI_OMIT_FLASHPOINT - -#endif - - #ifndef CONFIG_SCSI_OMIT_FLASHPOINT #define UNIX #define FW_TYPE _SCCB_MGR_ #define MAX_CARDS 8 +#undef BUSTYPE_PCI -#include - #define OS_InPortByte(port) inb(port) #define OS_InPortWord(port) inw(port) #define OS_InPortLong(port) inl(port) @@ -68,7 +43,7 @@ */ #define SccbMgr_sense_adapter FlashPoint_ProbeHostAdapter -#define SccbMgr_config_adapter FlashPoint_HardResetHostAdapter +#define SccbMgr_config_adapter FlashPoint_HardwareResetHostAdapter #define SccbMgr_unload_card FlashPoint_ReleaseHostAdapter #define SccbMgr_start_sccb FlashPoint_StartCCB #define SccbMgr_abort_sccb FlashPoint_AbortCCB @@ -169,6 +144,7 @@ #define stwidn FPT_stwidn #define sxfrp FPT_sxfrp #define utilEERead FPT_utilEERead +#define utilEEReadOrg FPT_utilEEReadOrg #define utilEESendCmdAddr FPT_utilEESendCmdAddr #define utilEEWrite FPT_utilEEWrite #define utilEEWriteOnOff FPT_utilEEWriteOnOff @@ -1317,9 +1293,9 @@ * * Description: Register definitions for HARPOON ASIC. * - * $Date: 1997/01/31 02:14:28 $ + * $Date: 1997/07/09 21:44:36 $ * - * $Revision: 1.6 $ + * $Revision: 1.9 $ * *----------------------------------------------------------------------*/ @@ -2070,9 +2046,14 @@ UCHAR RdStack(USHORT port, UCHAR index); void WrStack(USHORT portBase, UCHAR index, UCHAR data); UCHAR ChkIfChipInitialized(USHORT ioPort); + +#if defined(V302) UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun); +#endif + void SendMsg(USHORT port, UCHAR message); void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code); +UCHAR scsellDOS(USHORT p_port, UCHAR targ_id); #else UCHAR sfm(ULONG port, PSCCB pcurrSCCB); void scsiStartAuto(ULONG port); @@ -2090,7 +2071,11 @@ UCHAR RdStack(ULONG port, UCHAR index); void WrStack(ULONG portBase, UCHAR index, UCHAR data); UCHAR ChkIfChipInitialized(ULONG ioPort); + +#if defined(V302) UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tar, PUCHAR lun); +#endif + void SendMsg(ULONG port, UCHAR message); void queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code); #endif @@ -2130,6 +2115,7 @@ void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode); void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr); USHORT utilEERead(USHORT p_port, USHORT ee_addr); +USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr); void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr); #else void Wait1Second(ULONG p_port); @@ -2137,6 +2123,7 @@ void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode); void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr); USHORT utilEERead(ULONG p_port, USHORT ee_addr); +USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr); void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr); #endif @@ -2339,7 +2326,7 @@ extern unsigned int SccbGlobalFlags; -#ident "$Id: sccb.c 1.17 1997/02/11 21:06:41 mohan Exp $" +#ident "$Id: sccb.c 1.18 1997/06/10 16:47:04 mohan Exp $" /*---------------------------------------------------------------------- * * @@ -2353,9 +2340,9 @@ * Description: Functions relating to handling of the SCCB interface * between the device driver and the HARPOON. * - * $Date: 1997/02/11 21:06:41 $ + * $Date: 1997/06/10 16:47:04 $ * - * $Revision: 1.17 $ + * $Revision: 1.18 $ * *----------------------------------------------------------------------*/ @@ -2477,6 +2464,7 @@ if(ChkIfChipInitialized(ioport) == FALSE) { pCurrNvRam = NULL; + WR_HARPOON(ioport+hp_semaphore, 0x00); XbowInit(ioport, 0); /*Must Init the SCSI before attempting */ DiagEEPROM(ioport); } @@ -3036,6 +3024,7 @@ if(ChkIfChipInitialized(ioport) == FALSE) { pCurrNvRam = NULL; + WR_HARPOON(ioport+hp_semaphore, 0x00); XbowInit(ioport, 0); /*Must Init the SCSI before attempting */ DiagEEPROM(ioport); } @@ -4802,7 +4791,23 @@ may not show up if another device reselects us in 1.5us or less. SRR Wednesday, 3/8/1995. */ - while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) ; + while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL)) && + !((RDW_HARPOON((ioport+hp_intstat)) & PHASE) && + RD_HARPOON((ioport+hp_scsisig)) == + (SCSI_BSY | SCSI_REQ | SCSI_CD | SCSI_MSG | SCSI_IOBIT))) ; + + /* + The additional loop exit condition above detects a timing problem + with the revision D/E harpoon chips. The caller should reset the + host adapter to recover when 0xFE is returned. + */ + if (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) + { + mOS_Lock((PSCCBcard)pCurrCard); + MENABLE_INT(ioport); + mOS_UnLock((PSCCBcard)pCurrCard); + return 0xFE; + } WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC)); @@ -5347,7 +5352,7 @@ } #endif -#ident "$Id: sccb_dat.c 1.9 1997/01/31 02:12:58 mohan Exp $" +#ident "$Id: sccb_dat.c 1.10 1997/02/22 03:16:02 awin Exp $" /*---------------------------------------------------------------------- * * @@ -5361,9 +5366,9 @@ * Description: Functions relating to handling of the SCCB interface * between the device driver and the HARPOON. * - * $Date: 1997/01/31 02:12:58 $ + * $Date: 1997/02/22 03:16:02 $ * - * $Revision: 1.9 $ + * $Revision: 1.10 $ * *----------------------------------------------------------------------*/ @@ -5378,18 +5383,19 @@ /*#include */ /*#include */ +/* +** IMPORTANT NOTE!!! +** +** You MUST preassign all data to a valid value or zero. This is +** required due to the MS compiler bug under OS/2 and Solaris Real-Mode +** driver environment. +*/ + -#if defined(OS2) || defined (SOLARIS_REAL_MODE) -SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] = { 0 }; -SCCBCARD BL_Card[MAX_CARDS] = { 0 }; -SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR] = { 0 }; -NVRAMINFO nvRamInfo[MAX_MB_CARDS] = { 0 }; -#else -SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR]; -SCCBCARD BL_Card[MAX_CARDS]; -SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR]; -NVRAMINFO nvRamInfo[MAX_MB_CARDS]; -#endif +SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] = { { { 0 } } }; +SCCBCARD BL_Card[MAX_CARDS] = { { 0 } }; +SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR] = { { { 0 } } }; +NVRAMINFO nvRamInfo[MAX_MB_CARDS] = { { 0 } }; #if defined(OS2) @@ -5402,23 +5408,23 @@ #endif #if defined(DOS) -UCHAR first_time; +UCHAR first_time = 0; #endif -UCHAR mbCards; +UCHAR mbCards = 0; UCHAR scamHAString[] = {0x63, 0x07, 'B', 'U', 'S', 'L', 'O', 'G', 'I', 'C', \ ' ', 'B', 'T', '-', '9', '3', '0', \ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; -USHORT default_intena; +USHORT default_intena = 0; #if defined(BUGBUG) -UCHAR debug_int[MAX_CARDS][debug_size]; -UCHAR debug_index[MAX_CARDS]; -UCHAR reserved_1[3]; +UCHAR debug_int[MAX_CARDS][debug_size] = { 0 }; +UCHAR debug_index[MAX_CARDS] = { 0 }; +UCHAR reserved_1[3] = { 0 }; #endif -#ident "$Id: scsi.c 1.19 1997/01/31 02:08:14 mohan Exp $" +#ident "$Id: scsi.c 1.23 1997/07/09 21:42:54 mohan Exp $" /*---------------------------------------------------------------------- * * @@ -5433,9 +5439,9 @@ * selection/reselection, sync negotiation, message-in * decoding. * - * $Date: 1997/01/31 02:08:14 $ + * $Date: 1997/07/09 21:42:54 $ * - * $Revision: 1.19 $ + * $Revision: 1.23 $ * *----------------------------------------------------------------------*/ @@ -5483,11 +5489,13 @@ while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) && (TimeOutLoop++ < 20000) ){} + WR_HARPOON(port+hp_portctrl_0, SCSI_PORT); message = RD_HARPOON(port+hp_scsidata_0); - WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH)); + WR_HARPOON(port+hp_scsisig, SCSI_ACK + S_MSGI_PH); + if (TimeOutLoop > 20000) message = 0x00; /* force message byte = 0 if Time Out on Req */ @@ -5495,6 +5503,10 @@ if ((RDW_HARPOON((port+hp_intstat)) & PARITY) && (RD_HARPOON(port+hp_addstat) & SCSI_PAR_ERR)) { + WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH)); + WR_HARPOON(port+hp_xferstat, 0); + WR_HARPOON(port+hp_fiforead, 0); + WR_HARPOON(port+hp_fifowrite, 0); if (pCurrSCCB != NULL) { pCurrSCCB->Sccb_scsimsg = SMPARITY; @@ -5503,6 +5515,7 @@ do { ACCEPT_MSG_ATN(port); + TimeOutLoop = 0; while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) && (TimeOutLoop++ < 20000) ){} if (TimeOutLoop > 20000) @@ -5524,6 +5537,10 @@ }while(1); } + WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH)); + WR_HARPOON(port+hp_xferstat, 0); + WR_HARPOON(port+hp_fiforead, 0); + WR_HARPOON(port+hp_fifowrite, 0); return(message); } @@ -5894,12 +5911,20 @@ void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard) #endif { + +#if defined(V302) #ifdef DOS UCHAR our_target,message, msgRetryCount; extern UCHAR lun, tag; #else UCHAR our_target,message,lun,tag, msgRetryCount; #endif + +#else /* V302 */ + UCHAR our_target, message, lun = 0, tag, msgRetryCount; +#endif /* V302 */ + + PSCCBMgr_tar_info currTar_Info; PSCCB currSCCB; @@ -5972,7 +5997,103 @@ msgRetryCount = 0; do { + +#if defined(V302) + message = GetTarLun(port, p_card, our_target, pCurrCard, &tag, &lun); + +#else /* V302 */ + + currTar_Info = &sccbMgrTbl[p_card][our_target]; + tag = 0; + + + while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) + { + if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) + { + + WRW_HARPOON((port+hp_intstat), PHASE); + return; + } + } + + WRW_HARPOON((port+hp_intstat), PHASE); + if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH) + { + + message = sfm(port,pCurrCard->currentSCCB); + if (message) + { + + if (message <= (0x80 | LUN_MASK)) + { + lun = message & (UCHAR)LUN_MASK; + +#if !defined(DOS) + if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING) + { + if (currTar_Info->TarTagQ_Cnt != 0) + { + + if (!(currTar_Info->TarLUN_CA)) + { + ACCEPT_MSG(port); /*Release the ACK for ID msg. */ + + + message = sfm(port,pCurrCard->currentSCCB); + if (message) + { + ACCEPT_MSG(port); + } + + else + message = FALSE; + + if(message != FALSE) + { + tag = sfm(port,pCurrCard->currentSCCB); + + if (!(tag)) + message = FALSE; + } + + } /*C.A. exists! */ + + } /*End Q cnt != 0 */ + + } /*End Tag cmds supported! */ +#endif /* !DOS */ + + } /*End valid ID message. */ + + else + { + + ACCEPT_MSG_ATN(port); + } + + } /* End good id message. */ + + else + { + + message = FALSE; + } + } + else + { + ACCEPT_MSG_ATN(port); + + while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) && + !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) && + (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ; + + return; + } + +#endif /* V302 */ + if(message == FALSE) { msgRetryCount++; @@ -6071,6 +6192,8 @@ (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ; } +#if defined(V302) + #if defined(DOS) UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun) #else @@ -6162,6 +6285,7 @@ return(TRUE); } +#endif /* V302 */ #if defined(DOS) void SendMsg(USHORT port, UCHAR message) @@ -6469,6 +6593,10 @@ ACCEPT_MSG(port); WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START)); } + }else + { + if(pCurrSCCB->Sccb_scsimsg == SMPARITY) + WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START)); } } @@ -7386,6 +7514,7 @@ p_sccb->Sccb_scsistat = BUS_FREE_ST; p_sccb->SccbStatus = SCCB_IN_PROCESS; p_sccb->Sccb_scsimsg = SMNO_OP; + } @@ -9222,7 +9351,7 @@ currSCCB->Sccb_XferCnt = currSCCB->DataLength - currSCCB->Sccb_ATC; } } -#ident "$Id: scam.c 1.16 1997/01/31 02:11:12 mohan Exp $" +#ident "$Id: scam.c 1.17 1997/03/20 23:49:37 mohan Exp $" /*---------------------------------------------------------------------- * * @@ -9237,9 +9366,9 @@ * and the determination of the SCSI IDs to be assigned * to all perspective SCSI targets. * - * $Date: 1997/01/31 02:11:12 $ + * $Date: 1997/03/20 23:49:37 $ * - * $Revision: 1.16 $ + * $Revision: 1.17 $ * *----------------------------------------------------------------------*/ @@ -9468,7 +9597,7 @@ if (((ScamFlg & SCAM_ENABLED) && (scamInfo[i].state == LEGACY)) || (i != p_our_id)) { - scsell(p_port,i); + scsellDOS(p_port,i); } } #endif @@ -9885,6 +10014,7 @@ } if ((ret_data & 0x1F) == 0) + { /* if(bit_cnt != 0 || bit_cnt != 8) { @@ -9899,6 +10029,7 @@ return(0x00); else return(0xFF); + } } /*bit loop */ @@ -10090,6 +10221,90 @@ } } +#if defined(DOS) +/*--------------------------------------------------------------------- + * + * Function: scsell for DOS + * + * Description: Select the specified device ID using a selection timeout + * less than 2ms. This was specially required to solve + * the problem with Plextor 12X CD-ROM drive. This drive + * was responding the Selection at the end of 4ms and + * hanging the system. + * + *---------------------------------------------------------------------*/ + +UCHAR scsellDOS(USHORT p_port, UCHAR targ_id) +{ + USHORT i; + + WR_HARPOON(p_port+hp_page_ctrl, + (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE)); + + ARAM_ACCESS(p_port); + + WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER)); + WR_HARPOON(p_port+hp_seltimeout,TO_2ms); + + + for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) { + WRW_HARPOON(i, (MPM_OP+ACOMMAND)); + } + WRW_HARPOON(i, (BRH_OP+ALWAYS+ NP)); + + WRW_HARPOON((p_port+hp_intstat), + (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT)); + + WR_HARPOON(p_port+hp_select_id, targ_id); + + WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT); + WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT)); + WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL)); + + + while (!(RDW_HARPOON((p_port+hp_intstat)) & + (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {} + + if (RDW_HARPOON((p_port+hp_intstat)) & RESET) + Wait(p_port, TO_250ms); + + DISABLE_AUTO(p_port); + + WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER)); + WR_HARPOON(p_port+hp_seltimeout,TO_290ms); + + SGRAM_ACCESS(p_port); + + if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) { + + WRW_HARPOON((p_port+hp_intstat), + (RESET | TIMEOUT | SEL | BUS_FREE | PHASE)); + + WR_HARPOON(p_port+hp_page_ctrl, + (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)); + + return(FALSE); /*No legacy device */ + } + + else { + + while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) { + if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ) + { + WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH)); + ACCEPT_MSG(p_port); + } + } + + WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1); + + WR_HARPOON(p_port+hp_page_ctrl, + (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)); + + return(TRUE); /*Found one of them oldies! */ + } +} +#endif /* DOS */ /*--------------------------------------------------------------------- * @@ -10252,10 +10467,12 @@ match--; if (match == 0xFF) + { if (p_id_string[0] & BIT(5)) match = 7; else match = MAX_SCSI_TAR-1; + } } @@ -10300,10 +10517,12 @@ match--; if (match == 0xFF) + { if (p_id_string[0] & BIT(5)) match = 7; else match = MAX_SCSI_TAR-1; + } } return(NO_ID_AVAIL); @@ -10362,7 +10581,7 @@ utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM/2); utilEEWriteOnOff(p_port,0); /* Turn off write access */ } -#ident "$Id: diagnose.c 1.9 1997/01/31 02:09:48 mohan Exp $" +#ident "$Id: diagnose.c 1.10 1997/06/10 16:51:47 mohan Exp $" /*---------------------------------------------------------------------- * * @@ -10376,9 +10595,9 @@ * Description: Diagnostic funtions for testing the integrity of * the HARPOON. * - * $Date: 1997/01/31 02:09:48 $ + * $Date: 1997/06/10 16:51:47 $ * - * $Revision: 1.9 $ + * $Revision: 1.10 $ * *----------------------------------------------------------------------*/ @@ -10419,7 +10638,7 @@ WR_HARPOON(port+hp_scsireset,(DMA_RESET | HPSCSI_RESET | PROG_RESET | \ FIFO_CLR)); - WR_HARPOON(port+hp_scsireset,0x00); + WR_HARPOON(port+hp_scsireset,SCSI_INI); WR_HARPOON(port+hp_clkctrl_0,CLKCTRL_DEFAULT); @@ -10703,8 +10922,8 @@ temp += 0x70D3; utilEEWrite(p_port, 0x0010, BIOS_CONFIG/2); temp += 0x0010; - utilEEWrite(p_port, 0x0007, SCAM_CONFIG/2); - temp += 0x0007; + utilEEWrite(p_port, 0x0003, SCAM_CONFIG/2); + temp += 0x0003; utilEEWrite(p_port, 0x0007, ADAPTER_SCSI_ID/2); temp += 0x0007; @@ -10807,7 +11026,7 @@ } -#ident "$Id: utility.c 1.22 1997/01/31 02:12:23 mohan Exp $" +#ident "$Id: utility.c 1.23 1997/06/10 16:55:06 mohan Exp $" /*---------------------------------------------------------------------- * * @@ -10821,9 +11040,9 @@ * Description: Utility functions relating to queueing and EEPROM * manipulation and any other garbage functions. * - * $Date: 1997/01/31 02:12:23 $ + * $Date: 1997/06/10 16:55:06 $ * - * $Revision: 1.22 $ + * $Revision: 1.23 $ * *----------------------------------------------------------------------*/ /*#include */ @@ -11617,10 +11836,13 @@ ee_value &= ~SEE_DO; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value |= SEE_CLK; /* Clock data! */ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value &= ~SEE_CLK; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); } ee_value &= (EXT_ARB_ACK | SCSI_TERM_ENA_H); WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); @@ -11632,7 +11854,6 @@ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); /* Turn off Master Select */ } - /*--------------------------------------------------------------------- * * Function: Read EEPROM @@ -11648,6 +11869,40 @@ USHORT utilEERead(ULONG p_port, USHORT ee_addr) #endif { + USHORT i, ee_data1, ee_data2; + + i = 0; + ee_data1 = utilEEReadOrg(p_port, ee_addr); + do + { + ee_data2 = utilEEReadOrg(p_port, ee_addr); + + if(ee_data1 == ee_data2) + return(ee_data1); + + ee_data1 = ee_data2; + i++; + + }while(i < 4); + + return(ee_data1); +} + +/*--------------------------------------------------------------------- + * + * Function: Read EEPROM Original + * + * Description: Read a word from the EEPROM at the desired + * address. + * + *---------------------------------------------------------------------*/ + +#if defined(DOS) +USHORT utilEEReadOrg(USHORT p_port, USHORT ee_addr) +#else +USHORT utilEEReadOrg(ULONG p_port, USHORT ee_addr) +#endif +{ UCHAR ee_value; USHORT i, ee_data; @@ -11666,8 +11921,10 @@ ee_value |= SEE_CLK; /* Clock data! */ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value &= ~SEE_CLK; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_data <<= 1; @@ -11722,10 +11979,13 @@ ee_value &= ~SEE_DO; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value |= SEE_CLK; /* Clock data! */ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value &= ~SEE_CLK; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); } @@ -11744,10 +12004,13 @@ ee_value &= ~SEE_DO; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value |= SEE_CLK; /* Clock data! */ WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); ee_value &= ~SEE_CLK; WR_HARPOON(p_port+hp_ee_ctrl, ee_value); + WR_HARPOON(p_port+hp_ee_ctrl, ee_value); i >>= 1; } @@ -11783,6 +12046,114 @@ return(lrc); } + + +/* + The following inline definitions avoid type conflicts. +*/ + +static inline unsigned char +FlashPoint__ProbeHostAdapter(FlashPoint_Info_T *FlashPointInfo) +{ + return FlashPoint_ProbeHostAdapter((PSCCBMGR_INFO) FlashPointInfo); +} + + +static inline FlashPoint_CardHandle_T +FlashPoint__HardwareResetHostAdapter(FlashPoint_Info_T *FlashPointInfo) +{ + return FlashPoint_HardwareResetHostAdapter((PSCCBMGR_INFO) FlashPointInfo); +} + +static inline void +FlashPoint__ReleaseHostAdapter(FlashPoint_CardHandle_T CardHandle) +{ + FlashPoint_ReleaseHostAdapter(CardHandle); +} + + +static inline void +FlashPoint__StartCCB(FlashPoint_CardHandle_T CardHandle, BusLogic_CCB_T *CCB) +{ + FlashPoint_StartCCB(CardHandle, (PSCCB) CCB); +} + + +static inline void +FlashPoint__AbortCCB(FlashPoint_CardHandle_T CardHandle, BusLogic_CCB_T *CCB) +{ + FlashPoint_AbortCCB(CardHandle, (PSCCB) CCB); +} + + +static inline boolean +FlashPoint__InterruptPending(FlashPoint_CardHandle_T CardHandle) +{ + return FlashPoint_InterruptPending(CardHandle); +} + + +static inline int +FlashPoint__HandleInterrupt(FlashPoint_CardHandle_T CardHandle) +{ + return FlashPoint_HandleInterrupt(CardHandle); +} + + +#define FlashPoint_ProbeHostAdapter FlashPoint__ProbeHostAdapter +#define FlashPoint_HardwareResetHostAdapter FlashPoint__HardwareResetHostAdapter +#define FlashPoint_ReleaseHostAdapter FlashPoint__ReleaseHostAdapter +#define FlashPoint_StartCCB FlashPoint__StartCCB +#define FlashPoint_AbortCCB FlashPoint__AbortCCB +#define FlashPoint_InterruptPending FlashPoint__InterruptPending +#define FlashPoint_HandleInterrupt FlashPoint__HandleInterrupt + + +/* + FlashPoint_InquireTargetInfo returns the Synchronous Period, Synchronous + Offset, and Wide Transfers Active information for TargetID on CardHandle. +*/ + +void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T CardHandle, + int TargetID, + unsigned char *SynchronousPeriod, + unsigned char *SynchronousOffset, + unsigned char *WideTransfersActive) +{ + SCCBMGR_TAR_INFO *TargetInfo = + &sccbMgrTbl[((SCCBCARD *)CardHandle)->cardIndex][TargetID]; + if ((TargetInfo->TarSyncCtrl & SYNC_OFFSET) > 0) + { + *SynchronousPeriod = 5 * ((TargetInfo->TarSyncCtrl >> 5) + 1); + *SynchronousOffset = TargetInfo->TarSyncCtrl & SYNC_OFFSET; + } + else + { + *SynchronousPeriod = 0; + *SynchronousOffset = 0; + } + *WideTransfersActive = (TargetInfo->TarSyncCtrl & NARROW_SCSI ? 0 : 1); +} + + +#else /* CONFIG_SCSI_OMIT_FLASHPOINT */ + + +/* + Define prototypes for the FlashPoint SCCB Manager Functions. +*/ + +extern unsigned char FlashPoint_ProbeHostAdapter(FlashPoint_Info_T *); +extern FlashPoint_CardHandle_T + FlashPoint_HardwareResetHostAdapter(FlashPoint_Info_T *); +extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *); +extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *); +extern boolean FlashPoint_InterruptPending(FlashPoint_CardHandle_T); +extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T); +extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T); +extern void FlashPoint_InquireTargetInfo(FlashPoint_CardHandle_T, + int, unsigned char *, + unsigned char *, unsigned char *); #endif /* CONFIG_SCSI_OMIT_FLASHPOINT */ diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- v2.0.33/linux/drivers/scsi/Makefile Thu Aug 14 10:31:20 1997 +++ linux/drivers/scsi/Makefile Wed Jun 3 15:17:48 1998 @@ -370,12 +370,6 @@ include $(TOPDIR)/Rules.make -BusLogic.o: BusLogic.c FlashPoint.c - $(CC) $(CFLAGS) -c BusLogic.c -o BusLogic.O - $(CC) $(CFLAGS) -c FlashPoint.c -o FlashPoint.O - $(LD) -r -o BusLogic.o BusLogic.O FlashPoint.O - rm -f BusLogic.O FlashPoint.O - aha152x.o: aha152x.c $(CC) $(CFLAGS) $(AHA152X) -c aha152x.c diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c --- v2.0.33/linux/drivers/scsi/NCR5380.c Tue Oct 29 06:21:48 1996 +++ linux/drivers/scsi/NCR5380.c Wed Jun 3 15:17:48 1998 @@ -491,11 +491,11 @@ */ #ifndef USLEEP_SLEEP /* 20 ms (reasonable hard disk speed) */ -#define USLEEP_SLEEP 2 +#define USLEEP_SLEEP (20*HZ/1000) #endif /* 300 RPM (floppy speed) */ #ifndef USLEEP_POLL -#define USLEEP_POLL 20 +#define USLEEP_POLL (200*HZ/1000) #endif static struct Scsi_Host * expires_first = NULL; @@ -643,7 +643,7 @@ == 0)) trying_irqs |= mask; - timeout = jiffies + 25; + timeout = jiffies + 250*HZ/1000; probe_irq = IRQ_NONE; /* @@ -819,7 +819,7 @@ SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE); #endif - SPRINTF("\nBase Addr: 0x%05X ", (int)instance->base); + SPRINTF("\nBase Addr: 0x%05lX ", (long)instance->base); SPRINTF("io_port: %04x ", (int)instance->io_port); if (instance->irq == IRQ_NONE) SPRINTF("IRQ: None.\n"); @@ -1002,7 +1002,7 @@ case 5: printk("scsi%d: SCSI bus busy, waiting up to five seconds\n", instance->host_no); - timeout = jiffies + 500; + timeout = jiffies + 5*HZ; while (jiffies < timeout && (NCR5380_read(STATUS_REG) & SR_BSY)); break; case 2: @@ -1618,7 +1618,7 @@ * selection. */ - timeout = jiffies + 25; + timeout = jiffies + 250*HZ/1000; /* * XXX very interesting - we're seeing a bounce where the BSY we diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/NCR5380.h linux/drivers/scsi/NCR5380.h --- v2.0.33/linux/drivers/scsi/NCR5380.h Mon Apr 29 07:14:19 1996 +++ linux/drivers/scsi/NCR5380.h Wed Jun 3 15:17:48 1998 @@ -314,29 +314,33 @@ static int NCR5380_transfer_pio (struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data); -#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) && defined(i386) -static __inline__ int NCR5380_i386_dma_setup (struct Scsi_Host *instance, +#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) + +#if defined(i386) || defined(__alpha__) + +static __inline__ int NCR5380_pc_dma_setup (struct Scsi_Host *instance, unsigned char *ptr, unsigned int count, unsigned char mode) { unsigned limit; + unsigned long bus_addr = virt_to_bus(ptr); if (instance->dma_channel <=3) { if (count > 65536) count = 65536; - limit = 65536 - (((unsigned) ptr) & 0xFFFF); + limit = 65536 - (bus_addr & 0xFFFF); } else { if (count > 65536 * 2) count = 65536 * 2; - limit = 65536* 2 - (((unsigned) ptr) & 0x1FFFF); + limit = 65536* 2 - (bus_addr & 0x1FFFF); } if (count > limit) count = limit; - if ((count & 1) || (((unsigned) ptr) & 1)) + if ((count & 1) || (bus_addr & 1)) panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no); cli(); disable_dma(instance->dma_channel); clear_dma_ff(instance->dma_channel); - set_dma_addr(instance->dma_channel, (unsigned int) ptr); + set_dma_addr(instance->dma_channel, bus_addr); set_dma_count(instance->dma_channel, count); set_dma_mode(instance->dma_channel, mode); enable_dma(instance->dma_channel); @@ -344,17 +348,17 @@ return count; } -static __inline__ int NCR5380_i386_dma_write_setup (struct Scsi_Host *instance, +static __inline__ int NCR5380_pc_dma_write_setup (struct Scsi_Host *instance, unsigned char *src, unsigned int count) { - return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_WRITE); + return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_WRITE); } -static __inline__ int NCR5380_i386_dma_read_setup (struct Scsi_Host *instance, +static __inline__ int NCR5380_pc_dma_read_setup (struct Scsi_Host *instance, unsigned char *src, unsigned int count) { - return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_READ); + return NCR5380_pc_dma_setup (instance, src, count, DMA_MODE_READ); } -static __inline__ int NCR5380_i386_dma_residual (struct Scsi_Host *instance) { +static __inline__ int NCR5380_pc_dma_residual (struct Scsi_Host *instance) { register int tmp; cli(); clear_dma_ff(instance->dma_channel); @@ -362,7 +366,8 @@ sti(); return tmp; } -#endif /* defined(REAL_DMA) && defined(i386) */ +#endif /* defined(i386) || defined(__alpha__) */ +#endif /* defined(REAL_DMA) */ #endif __KERNEL_ #endif /* ndef ASM */ #endif /* NCR5380_H */ diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic --- v2.0.33/linux/drivers/scsi/README.BusLogic Mon Aug 11 00:10:00 1997 +++ linux/drivers/scsi/README.BusLogic Wed Jun 3 15:17:48 1998 @@ -1,41 +1,48 @@ BusLogic MultiMaster and FlashPoint SCSI Driver for Linux - Version 2.0.10 for Linux 2.0 + Version 2.0.14 for Linux 2.0 PRODUCTION RELEASE - 11 August 1997 + 29 April 1998 Leonard N. Zubkoff Dandelion Digital lnz@dandelion.com - Copyright 1995 by Leonard N. Zubkoff + Copyright 1995-1998 by Leonard N. Zubkoff INTRODUCTION -BusLogic, Inc. designs and manufactures a variety of high performance SCSI host -adapters which share a common programming interface across a diverse collection -of bus architectures by virtue of their MultiMaster ASIC technology. This -driver supports all present BusLogic MultiMaster Host Adapters, and should +BusLogic, Inc. designed and manufactured a variety of high performance SCSI +host adapters which share a common programming interface across a diverse +collection of bus architectures by virtue of their MultiMaster ASIC technology. +BusLogic was acquired by Mylex Corporation in February 1996, but the products +supported by this driver originated under the BusLogic name and so that name is +retained in the source code and documentation. + +This driver supports all present BusLogic MultiMaster Host Adapters, and should support any future MultiMaster designs with little or no modification. More -recently, BusLogic has introduced the FlashPoint Host Adapters, which are less +recently, BusLogic introduced the FlashPoint Host Adapters, which are less costly and rely on the host CPU, rather than including an onboard processor. -Mylex/BusLogic has recently provided me with the FlashPoint Driver Developer's -Kit, which comprises documentation and freely redistributable source code for -the FlashPoint SCCB Manager. The SCCB Manager is the library of code that runs -on the host CPU and performs functions analogous to the firmware on the -MultiMaster Host Adapters. Thanks to their having provided the SCCB Manager, -this driver now supports the FlashPoint Host Adapters as well. +Despite not having an onboard CPU, the FlashPoint Host Adapters perform very +well and have very low command latency. BusLogic has recently provided me with +the FlashPoint Driver Developer's Kit, which comprises documentation and freely +redistributable source code for the FlashPoint SCCB Manager. The SCCB Manager +is the library of code that runs on the host CPU and performs functions +analogous to the firmware on the MultiMaster Host Adapters. Thanks to their +having provided the SCCB Manager, this driver now supports the FlashPoint Host +Adapters as well. My primary goals in writing this completely new BusLogic driver for Linux are to achieve the full performance that BusLogic SCSI Host Adapters and modern SCSI peripherals are capable of, and to provide a highly robust driver that can be depended upon for high performance mission critical applications. All of the major performance and error recovery features can be configured from the -Linux kernel command line, allowing individual installations to tune driver -performance and error recovery to their particular needs. +Linux kernel command line or at module initialization time, allowing individual +installations to tune driver performance and error recovery to their particular +needs. The latest information on Linux support for BusLogic SCSI Host Adapters, as well as the most recent release of this driver and the latest firmware for the @@ -48,33 +55,35 @@ relevant to SCSI operations, and a detailed description of your system's hardware configuration. -BusLogic has been an excellent company to work with and I highly recommend -their products to the Linux community. In November 1995, I was offered the +Mylex has been an excellent company to work with and I highly recommend their +products to the Linux community. In November 1995, I was offered the opportunity to become a beta test site for their latest MultiMaster product, the BT-948 PCI Ultra SCSI Host Adapter, and then again for the BT-958 PCI Wide Ultra SCSI Host Adapter in January 1996. This was mutually beneficial since -BusLogic received a degree and kind of testing that their own testing group -cannot readily achieve, and the Linux community has available high performance -host adapters that have been well tested with Linux even before being brought -to market. This relationship has also given me the opportunity to interact +Mylex received a degree and kind of testing that their own testing group cannot +readily achieve, and the Linux community has available high performance host +adapters that have been well tested with Linux even before being brought to +market. This relationship has also given me the opportunity to interact directly with their technical staff, to understand more about the internal workings of their products, and in turn to educate them about the needs and -potential of the Linux community. Their interest and support is greatly -appreciated. +potential of the Linux community. + +More recently, Mylex has reaffirmed the company's interest in supporting the +Linux community, and I am now working on a Linux driver for the DAC960 PCI RAID +Controllers. Mylex's interest and support is greatly appreciated. -Unlike some other vendors, if you contact BusLogic Technical Support with a +Unlike some other vendors, if you contact Mylex Technical Support with a problem and are running Linux, they will not tell you that your use of their products is unsupported. Their latest product marketing literature even states -"BusLogic SCSI host adapters are compatible with all major operating systems +"Mylex SCSI host adapters are compatible with all major operating systems including: ... Linux ...". -BusLogic, Inc. is located at 4151 Burton Drive, Santa Clara, California, 95054, -USA and can be reached by Voice at 408/492-9090 or by FAX at 408/492-1542. -BusLogic maintains a World Wide Web site at http://www.buslogic.com, an -anonymous FTP site at ftp.buslogic.com, and a BBS at 408/492-1984. BusLogic -Technical Support can be reached by electronic mail at techsup@buslogic.com, by -Voice at 408/654-0760, or by FAX at 408/492-1542. Contact information for -offices in Europe and Japan is available on the Web site. +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 Technical Support can be reached by electronic +mail at techsup@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. DRIVER FEATURES @@ -83,15 +92,46 @@ During system initialization, the driver reports extensively on the host adapter hardware configuration, including the synchronous transfer parameters - negotiated with each target device. In addition, the driver tests the - hardware interrupt configuration to verify that interrupts are actually - delivered correctly to the interrupt handler. This should catch a high - percentage of PCI motherboard configuration errors early, because when the - host adapter is probed successfully, most of the remaining problems appear to - be related to interrupts. Most often, any remaining hardware problems are - related to the specific configuration of devices on the SCSI bus, and the - quality of cabling and termination used. Finally, this BusLogic driver - should never incorrectly attempt to support an Adaptec 154x Host Adapter. + requested and negotiated with each target device. AutoSCSI settings for + Synchronous Negotiation, Wide Negotiation, and Disconnect/Reconnect are + reported for each target device, as well as the status of Tagged Queuing and + Error Recovery. If the same setting is in effect for all target devices, + then a single word or phrase is used; otherwise, a letter is provided for + each target device to indicate the individual status. The following examples + should clarify this reporting format: + + Synchronous Negotiation: Ultra + + Synchronous negotiation is enabled for all target devices and the host + adapter will attempt to negotiate for 20.0 mega-transfers/second. + + Synchronous Negotiation: Fast + + Synchronous negotiation is enabled for all target devices and the host + adapter will attempt to negotiate for 10.0 mega-transfers/second. + + Synchronous Negotiation: Slow + + Synchronous negotiation is enabled for all target devices and the host + adapter will attempt to negotiate for 5.0 mega-transfers/second. + + Synchronous Negotiation: Disabled + + Synchronous negotiation is disabled and all target devices are limited to + asynchronous operation. + + Synchronous Negotiation: UFSNUUU#UUUUUUUU + + Synchronous negotiation to Ultra speed is enabled for target devices 0 + and 4 through 15, to Fast speed for target device 1, to Slow speed for + target device 2, and is not permitted to target device 3. The host + adapter's SCSI ID is represented by the "#". + + The status of Wide Negotiation, Disconnect/Reconnect, and Tagged Queuing + are reported as "Enabled", Disabled", or a sequence of "Y" and "N" letters. + + The Error Recovery option is reported as "Default", "Hard Reset", + "Bus Device Reset", "None" or a sequence of "D", "H", "B", and "N" letters. o Performance Features @@ -103,16 +143,15 @@ addition, BusLogic's Strict Round Robin Mode is used to optimize host adapter performance, and scatter/gather I/O can support as many segments as can be effectively utilized by the Linux I/O subsystem. Control over the use of - tagged queuing for each target device as well as selection of the tagged - queue depth is available from the kernel command line. By default, the queue - depth is automatically determined based on the number, type, speed, and - capabilities of the target devices found. In addition, tagged queuing is - automatically disabled whenever the host adapter firmware version is known - not to implement it correctly, or whenever a tagged queue depth of 1 is - selected. Tagged queuing is also disabled for individual target devices if - disconnect/reconnect is disabled for that device. In performance testing, - sustained disk writes of 7.3MB per second have been observed to a /dev/sd - device. + tagged queuing for each target device as well as individual selection of the + tagged queue depth is available through driver options provided on the kernel + command line or at module initialization time. By default, the queue depth + is determined automatically based on the host adapter's total queue depth and + the number, type, speed, and capabilities of the target devices found. In + addition, tagged queuing is automatically disabled whenever the host adapter + firmware version is known not to implement it correctly, or whenever a tagged + queue depth of 1 is selected. Tagged queuing is also disabled for individual + target devices if disconnect/reconnect is disabled for that device. o Robustness Features @@ -121,15 +160,15 @@ a selection is made between a full host adapter hard reset and SCSI bus reset versus sending a bus device reset message to the individual target device based on the recommendation of the SCSI subsystem. Error recovery strategies - are selectable from the kernel command line individually for each target - device, and also include sending a bus device reset to the specific target - device associated with the command being reset, as well as suppressing error + are selectable through driver options individually for each target device, + and also include sending a bus device reset to the specific target device + associated with the command being reset, as well as suppressing error recovery entirely to avoid perturbing an improperly functioning device. If the bus device reset error recovery strategy is selected and sending a bus device reset does not restore correct operation, the next command that is reset will force a full host adapter hard reset and SCSI bus reset. SCSI bus resets caused by other devices and detected by the host adapter are also - handled by issuing a hard reset to the host adapter and re-initialization. + handled by issuing a soft reset to the host adapter and re-initialization. Finally, if tagged queuing is active and more than one command reset occurs in a 10 minute interval, or if a command reset occurs within the first 10 minutes of operation, then tagged queuing will be disabled for that target @@ -138,15 +177,6 @@ lock up or crash, and thereby allowing a clean shutdown and restart after the offending component is removed. -o Extensive Testing - - This driver has undergone extensive testing and improvement over a period of - several months, and is routinely being used on heavily loaded systems. Over - 300 people retrieved the driver during the beta test period. In addition to - testing in normal system operation, error recovery tests have been performed - to verify proper system recovery in the case of simulated dropped interrupts, - external SCSI bus resets, and SCSI command errors due to bad CD-ROM media. - o PCI Configuration Support On PCI systems running kernels compiled with PCI BIOS support enabled, this @@ -159,8 +189,8 @@ o /proc File System Support - Copies of the host adapter configuration information together with data - transfer and error recovery statistics are now available through the + Copies of the host adapter configuration information together with updated + data transfer and error recovery statistics are available through the /proc/scsi/BusLogic/ interface. o Shared Interrupts Support @@ -168,16 +198,6 @@ On systems that support shared interrupts, any number of BusLogic Host Adapters may share the same interrupt request channel. -o Wide SCSI Support - - All BusLogic MultiMaster SCSI Host Adapters share a common programming - interface, except for the inevitable improvements and extensions as new - models are released, so support for Wide SCSI data transfer has automatically - been available without explicit driver support. When used with Linux 2.0.x, - this driver adds explicit support for up to 15 target devices and 64 logical - units per target device, to fully exploit the capabilities of the newest - BusLogic Wide SCSI Host Adapters. - SUPPORTED HOST ADAPTERS @@ -188,16 +208,21 @@ FlashPoint Series PCI Host Adapters: -FlashPoint LT (BT-930) Ultra SCSI-2 -FlashPoint DL (BT-932) Dual Channel Ultra SCSI-2 -FlashPoint LW (BT-950) Wide Ultra SCSI-2 -FlashPoint DW (BT-952) Dual Channel Wide Ultra SCSI-2 +FlashPoint LT (BT-930) Ultra SCSI-3 +FlashPoint LT (BT-930R) Ultra SCSI-3 with RAIDPlus +FlashPoint LT (BT-920) Ultra SCSI-3 (BT-930 without BIOS) +FlashPoint DL (BT-932) Dual Channel Ultra SCSI-3 +FlashPoint DL (BT-932R) Dual Channel Ultra SCSI-3 with RAIDPlus +FlashPoint LW (BT-950) Wide Ultra SCSI-3 +FlashPoint LW (BT-950R) Wide Ultra SCSI-3 with RAIDPlus +FlashPoint DW (BT-952) Dual Channel Wide Ultra SCSI-3 +FlashPoint DW (BT-952R) Dual Channel Wide Ultra SCSI-3 with RAIDPlus MultiMaster "W" Series Host Adapters: -BT-948 PCI Ultra SCSI-2 -BT-958 PCI Wide Ultra SCSI-2 -BT-958D PCI Wide Differential Ultra SCSI-2 +BT-948 PCI Ultra SCSI-3 +BT-958 PCI Wide Ultra SCSI-3 +BT-958D PCI Wide Differential Ultra SCSI-3 MultiMaster "C" Series Host Adapters: @@ -231,6 +256,39 @@ AMI FastDisk Host Adapters that are true BusLogic MultiMaster clones are also supported by this driver. +BusLogic SCSI Host Adapters are available packaged both as bare boards and as +retail kits. The BT- model numbers above refer to the bare board packaging. +The retail kit model numbers are found by replacing BT- with KT- in the above +list. The retail kit includes the bare board and manual as well as cabling and +driver media and documentation that are not provided with bare boards. + + + FLASHPOINT INSTALLATION NOTES + +o RAIDPlus Support + + FlashPoint Host Adapters now include RAIDPlus, Mylex's bootable software + RAID. RAIDPlus is not supported on Linux, and there are no plans to support + it. The MD driver in Linux 2.0 provides for concatenation (LINEAR) and + striping (RAID-0), and support for mirroring (RAID-1), fixed parity (RAID-4), + and distributed parity (RAID-5) is available separately. The built-in Linux + RAID support is generally more flexible and is expected to perform better + than RAIDPlus, so there is little impetus to include RAIDPlus support in the + BusLogic driver. + +o Enabling UltraSCSI Transfers + + FlashPoint Host Adapters ship with their configuration set to "Factory + Default" settings that are conservative and do not allow for UltraSCSI speed + to be negotiated. This results in fewer problems when these host adapters + are installed in systems with cabling or termination that is not sufficient + for UltraSCSI operation, or where existing SCSI devices do not properly + respond to synchronous transfer negotiation for UltraSCSI speed. AutoSCSI + may be used to load "Optimum Performance" settings which allow UltraSCSI + speed to be negotiated with all devices, or UltraSCSI speed can be enabled on + an individual basis. It is recommended that SCAM be manually disabled after + the "Optimum Performance" settings are loaded. + BT-948/958/958D INSTALLATION NOTES @@ -284,113 +342,258 @@ so as to recognize the host adapters in the same order as they are enumerated by the host adapter's BIOS. -o Mega-Transfers/Second +o Enabling UltraSCSI Transfers + + The BT-948/958/958D ship with their configuration set to "Factory Default" + settings that are conservative and do not allow for UltraSCSI speed to be + negotiated. This results in fewer problems when these host adapters are + installed in systems with cabling or termination that is not sufficient for + UltraSCSI operation, or where existing SCSI devices do not properly respond + to synchronous transfer negotiation for UltraSCSI speed. AutoSCSI may be + used to load "Optimum Performance" settings which allow UltraSCSI speed to be + negotiated with all devices, or UltraSCSI speed can be enabled on an + individual basis. It is recommended that SCAM be manually disabled after the + "Optimum Performance" settings are loaded. + + + DRIVER OPTIONS + +BusLogic Driver Options may be specified either via the Linux Kernel Command +Line or via the Loadable Kernel Module Installation Facility. Driver Options +for multiple host adapters may be specified either by separating the option +strings by a semicolon, or by specifying multiple "BusLogic=" strings on the +command line. Individual option specifications for a single host adapter are +separated by commas. The Probing and Debugging Options apply to all host +adapters whereas the remaining options apply individually only to the +selected host adapter. + +The BusLogic Driver Probing Options comprise the following: + +IO: + + The "IO:" option specifies an ISA I/O Address to be probed for a non-PCI + MultiMaster Host Adapter. If neither "IO:" nor "NoProbeISA" options are + specified, then the standard list of BusLogic MultiMaster ISA I/O Addresses + will be probed (0x330, 0x334, 0x230, 0x234, 0x130, and 0x134). Multiple + "IO:" options may be specified to precisely determine the I/O Addresses to + be probed, but the probe order will always follow the standard list. + +NoProbe + + The "NoProbe" option disables all probing and therefore no BusLogic Host + Adapters will be detected. + +NoProbeISA + + The "NoProbeISA" option disables probing of the standard BusLogic ISA I/O + Addresses and therefore only PCI MultiMaster and FlashPoint Host Adapters + will be detected. - The driver reports on the synchronous transfer parameters negotiated between - the host adapter and target devices in units of "mega-transfers/second". For - wide devices, the unit of transfer is 16 bits if wide negotiation has been - successfully completed. Therefore, the total transfer rate to wide devices - will generally be twice the synchronous tranfer rate reported by the driver. +NoProbePCI + The "NoProbePCI" options disables the interrogation of PCI Configuration + Space and therefore only ISA Multimaster Host Adapters will be detected, as + well as PCI Multimaster Host Adapters that have their ISA Compatible I/O + Port set to "Primary" or "Alternate". - COMMAND LINE OPTIONS +NoSortPCI -Many features of this driver are configurable by specification of appropriate -kernel command line options. A full description of the command line options -may be found in the comments before BusLogic_Setup in the kernel source code -file "BusLogic.c". The following examples may be useful as a starting point: + The "NoSortPCI" option forces PCI MultiMaster Host Adapters to be + enumerated in the order provided by the PCI BIOS, ignoring any setting of + the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option. - "BusLogic=NoProbe" +MultiMasterFirst - No probing of any kind is to be performed, and hence no BusLogic Host - Adapters will be detected. + The "MultiMasterFirst" option forces MultiMaster Host Adapters to be probed + before FlashPoint Host Adapters. By default, if both FlashPoint and PCI + MultiMaster Host Adapters are present, this driver will probe for + FlashPoint Host Adapters first unless the BIOS primary disk is controlled + by the first PCI MultiMaster Host Adapter, in which case MultiMaster Host + Adapters will be probed first. - "BusLogic=NoProbeISA" +FlashPointFirst - No probing of the standard ISA I/O Addresses will be done, and hence only - PCI Host Adapters will be detected. + The "FlashPointFirst" option forces FlashPoint Host Adapters to be probed + before MultiMaster Host Adapters. - "BusLogic=NoProbePCI" +The BusLogic Driver Tagged Queuing Options allow for explicitly specifying +the Queue Depth and whether Tagged Queuing is permitted for each Target +Device (assuming that the Target Device supports Tagged Queuing). The Queue +Depth is the number of SCSI Commands that are allowed to be concurrently +presented for execution (either to the Host Adapter or Target Device). Note +that explicitly enabling Tagged Queuing may lead to problems; the option to +enable or disable Tagged Queuing is provided primarily to allow disabling +Tagged Queuing on Target Devices that do not implement it correctly. The +following options are available: - No interrogation of PCI Configuration Space will be made, and hence only - ISA Multimaster Host Adapters will be detected, as well as PCI Multimaster - Host Adapters that have their ISA Compatible I/O Port set to "Primary" or - "Alternate". +QueueDepth: - "BusLogic=NoSortPCI" + The "QueueDepth:" or QD:" option specifies the Queue Depth to use for all + Target Devices that support Tagged Queuing, as well as the maximum Queue + Depth for devices that do not support Tagged Queuing. If no Queue Depth + option is provided, the Queue Depth will be determined automatically based + on the Host Adapter's Total Queue Depth and the number, type, speed, and + capabilities of the detected Target Devices. For Host Adapters that + require ISA Bounce Buffers, the Queue Depth is automatically set by default + to BusLogic_TaggedQueueDepthBB or BusLogic_UntaggedQueueDepthBB to avoid + excessive preallocation of DMA Bounce Buffer memory. Target Devices that + do not support Tagged Queuing always have their Queue Depth set to + BusLogic_UntaggedQueueDepth or BusLogic_UntaggedQueueDepthBB, unless a + lower Queue Depth option is provided. A Queue Depth of 1 automatically + disables Tagged Queuing. - PCI MultiMaster Host Adapters will be enumerated in the order provided by - the PCI BIOS, ignoring any setting of the AutoSCSI "Use Bus And Device # - For PCI Scanning Seq." option. +QueueDepth:[,...] - "BusLogic=MultiMasterFirst" + The "QueueDepth:[...]" or "QD:[...]" option specifies the Queue Depth + individually for each Target Device. If an is omitted, the + associated Target Device will have its Queue Depth selected automatically. - By default, if both FlashPoint and PCI MultiMaster Host Adapters are - present, this driver will probe for FlashPoint Host Adapters first unless - the BIOS primary disk is controlled by the first PCI MultiMaster Host - Adapter, in which case MultiMaster Host Adapters will be probed first. - This option forces MultiMaster Host Adapters to be probed first. +TaggedQueuing:Default - "BusLogic=FlashPointFirst" + The "TaggedQueuing:Default" or "TQ:Default" option permits Tagged Queuing + based on the firmware version of the BusLogic Host Adapter and based on + whether the Queue Depth allows queuing multiple commands. - By default, if both FlashPoint and PCI MultiMaster Host Adapters are - present, this driver will probe for FlashPoint Host Adapters first unless - the BIOS primary disk is controlled by the first PCI MultiMaster Host - Adapter, in which case MultiMaster Host Adapters will be probed first. - This option forces FlashPoint Host Adapters to be probed first. +TaggedQueuing:Enable - "BusLogic=0x330" + The "TaggedQueuing:Enable" or "TQ:Enable" option enables Tagged Queuing for + all Target Devices on this Host Adapter, overriding any limitation that + would otherwise be imposed based on the Host Adapter firmware version. - This command line limits probing to the single I/O port at 0x330. +TaggedQueuing:Disable - "BusLogic=0,1" + The "TaggedQueuing:Disable" or "TQ:Disable" option disables Tagged Queuing + for all Target Devices on this Host Adapter. - This command line selects default probing and a tagged queue depth of 1 - which also disables tagged queuing. It may be useful if problems arise - during installation on a system with a flaky SCSI configuration. In cases - of a marginal SCSI configuration it may also be beneficial to disable fast - transfers and/or synchronous negotiation using AutoSCSI on FlashPoint and - "W" and "C" series MultiMaster host adapters. Disconnect/reconnect may - also be disabled for fast devices such as disk drives, but should not be - disabled for tape drives or other devices where a single command may take - over a second to execute. +TaggedQueuing: - "BusLogic=0,0,30" + The "TaggedQueuing:" or "TQ:" option controls + Tagged Queuing individually for each Target Device. is a + sequence of "Y", "N", and "X" characters. "Y" enables Tagged Queuing, "N" + disables Tagged Queuing, and "X" accepts the default based on the firmware + version. The first character refers to Target Device 0, the second to + Target Device 1, and so on; if the sequence of "Y", "N", and "X" characters + does not cover all the Target Devices, unspecified characters are assumed + to be "X". - This command line selects default probing and automatic tagged queue depth - selection, but changes the bus settle time to 30 seconds. It may be useful - with SCSI devices that take an unusually long time to become ready to - accept commands after a SCSI bus reset. Some tape drives will not respond - properly immediately after a SCSI bus reset, especially if a tape is - present in the drive. +The BusLogic Driver Error Recovery Option allows for explicitly specifying +the Error Recovery action to be performed when BusLogic_ResetCommand is +called due to a SCSI Command failing to complete successfully. The following +options are available: - "BusLogic=TQ:Disable" +ErrorRecovery:Default - This command line selects default probing and disables tagged queuing. + The "ErrorRecovery:Default" or "ER:Default" option selects between the Hard + Reset and Bus Device Reset options based on the recommendation of the SCSI + Subsystem. - "BusLogic=0,15,TQ:N" +ErrorRecovery:HardReset - This command line selects a tagged queue depth of 15 and disables tagged - queuing for target 0, while allowing tagged queuing for all other target - devices. + The "ErrorRecovery:HardReset" or "ER:HardReset" option will initiate a Host + Adapter Hard Reset which also causes a SCSI Bus Reset. -Note that limiting the tagged queue depth or disabling tagged queuing can -substantially impact performance. +ErrorRecovery:BusDeviceReset + The "ErrorRecovery:BusDeviceReset" or "ER:BusDeviceReset" option will send + a Bus Device Reset message to the individual Target Device causing the + error. If Error Recovery is again initiated for this Target Device and no + SCSI Command to this Target Device has completed successfully since the Bus + Device Reset message was sent, then a Hard Reset will be attempted. - INSTALLATION +ErrorRecovery:None -This distribution was prepared for Linux kernel version 2.0.30, but should be -compatible with 2.0.4 or any later 2.0 series kernel if BusLogic.patch is also -applied. + The "ErrorRecovery:None" or "ER:None" option suppresses Error Recovery. + This option should only be selected if a SCSI Bus Reset or Bus Device Reset + will cause the Target Device or a critical operation to suffer a complete + and unrecoverable failure. + +ErrorRecovery: + + The "ErrorRecovery:" or "ER:" option controls + Error Recovery individually for each Target Device. is a + sequence of "D", "H", "B", and "N" characters. "D" selects Default, "H" + selects Hard Reset, "B" selects Bus Device Reset, and "N" selects None. + The first character refers to Target Device 0, the second to Target Device + 1, and so on; if the sequence of "D", "H", "B", and "N" characters does not + cover all the possible Target Devices, unspecified characters are assumed + to be "D". + +The BusLogic Driver Miscellaneous Options comprise the following: + +BusSettleTime: + + The "BusSettleTime:" or "BST:" option specifies the Bus Settle Time in + seconds. The Bus Settle Time is the amount of time to wait between a Host + Adapter Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI + Commands. If unspecified, it defaults to BusLogic_DefaultBusSettleTime. + +InhibitTargetInquiry + + The "InhibitTargetInquiry" option inhibits the execution of an Inquire + Target Devices or Inquire Installed Devices command on MultiMaster Host + Adapters. This may be necessary with some older Target Devices that do not + respond correctly when Logical Units above 0 are addressed. + +The BusLogic Driver Debugging Options comprise the following: + +TraceProbe + + The "TraceProbe" option enables tracing of Host Adapter Probing. + +TraceHardwareReset + + The "TraceHardwareReset" option enables tracing of Host Adapter Hardware + Reset. + +TraceConfiguration + + The "TraceConfiguration" option enables tracing of Host Adapter + Configuration. + +TraceErrors + + The "TraceErrors" option enables tracing of SCSI Commands that return an + error from the Target Device. The CDB and Sense Data will be printed for + each SCSI Command that fails. + +Debug + + The "Debug" option enables all debugging options. + +The following examples demonstrate setting the Queue Depth for Target Devices +1 and 2 on the first host adapter to 7 and 15, the Queue Depth for all Target +Devices on the second host adapter to 31, and the Bus Settle Time on the +second host adapter to 30 seconds. + +Linux Kernel Command Line: + + linux BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30 + +LILO Linux Boot Loader (in /etc/lilo.conf): + + append = "BusLogic=QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30" + +INSMOD Loadable Kernel Module Installation Facility: + + insmod BusLogic.o \ + 'BusLogic_Options="QueueDepth:[,7,15];QueueDepth:31,BusSettleTime:30"' + +NOTE: Module Utilities 2.1.71 or later is required for correct parsing + of driver options containing commas. + + + DRIVER INSTALLATION + +This distribution was prepared for Linux kernel version 2.0.33, but should be +compatible with 2.0.4 or any later 2.0 series kernel. To install the new BusLogic SCSI driver, you may use the following commands, replacing "/usr/src" with wherever you keep your Linux kernel source tree: cd /usr/src - tar -xvzf BusLogic-2.0.10.tar.gz + tar -xvzf BusLogic-2.0.14.tar.gz mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi - patch -p < BusLogic.patch # Only for kernels prior to 2.0.30 + patch -p < BusLogic.patch cd linux make config make depend @@ -398,11 +601,6 @@ Then install "arch/i386/boot/zImage" as your standard kernel, run lilo if appropriate, and reboot. - -Be sure to answer "y" to the "BusLogic SCSI support" query during the "make -config" step. If your system was already configured for the old BusLogic -driver or for an older version of this driver, you may omit the "make config" -step above. BUSLOGIC ANNOUNCEMENTS MAILING LIST diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/README.Mylex linux/drivers/scsi/README.Mylex --- v2.0.33/linux/drivers/scsi/README.Mylex Wed Dec 31 16:00:00 1969 +++ linux/drivers/scsi/README.Mylex Wed Jun 3 15:17:48 1998 @@ -0,0 +1,6 @@ +Please see the file README.BusLogic for information about Linux support for +Mylex (formerly BusLogic) MultiMaster and FlashPoint SCSI Host Adapters. + +The Mylex DAC960 PCI RAID Controllers are not supported at the present time, +but work on a Linux driver for the DAC960 is in progress. Please consult +http://www.dandelion.com/Linux/ for further information on the DAC960 driver. diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx --- v2.0.33/linux/drivers/scsi/README.aic7xxx Sat Apr 20 10:59:10 1996 +++ linux/drivers/scsi/README.aic7xxx Wed Jun 3 15:17:48 1998 @@ -1,8 +1,7 @@ AIC7xxx Driver for Linux - April 15, 1996 Introduction ------------------------- +---------------------------- The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com) SCSI controllers and chipsets. Major portions of the driver and driver development are shared between both Linux and FreeBSD. Support for the @@ -11,31 +10,42 @@ 2.1.0 or later. Supported cards/chipsets - ------------------------ + ---------------------------- Adaptec Cards - ----------------------- - AHA-274x - AHA-2842 + ---------------------------- + AHA-274x + AHA-274xT + AHA-2842 + AHA-2910B AHA-2940 AHA-2940W AHA-2940U - AHA-2940UW + AHA-2940UW + AHA-2940AU AHA-2944D AHA-2944WD + AHA-2944UD + AHA-2944UWD AHA-3940 + AHA-3940U AHA-3940W + AHA-3940UW AHA-3985 + AHA-3985U AHA-3985W + AHA-3985UW Motherboard Chipsets - ----------------------- + ---------------------------- AIC-777x AIC-785x + AIC-786x AIC-787x AIC-788x + AIC-7895 Bus Types - ----------------------- + ---------------------------- W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s. U - Ultra SCSI, transfer rates up to 40MB/s. @@ -49,22 +59,28 @@ AHA-398x - PCI RAID controllers with three separate SCSI controllers on-board. - NOTE: The AHA-2920 is NOT a AIC-7xxx based controller, and is not + NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not handled by this driver. People - ------------------------ - Justin T Gibbs gibbs@freefall.FreeBSD.org (BSD Driver Author) - Dan Eischen deischen@iworks.InterWorks.org (Linux Driver Co-maintainer) - Dean Gehnert deang@teleport.com (Linux FTP/patch maintainer) - Jess Johnson jester@frenzy.com (AIC7xxx FAQ author) - + ------------------------------ + Justin T Gibbs gibbs@plutotech.com + (BSD Driver Author) + Dan Eischen deischen@iworks.InterWorks.org + (Original Linux Driver Co-maintainer) + Dean Gehnert deang@teleport.com + (Original Linux FTP/patch maintainer) + Jess Johnson jester@frenzy.com + (AIC7xxx FAQ author) + Doug Ledford dledford@dialnet.net + (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer) + Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original author of the driver. John has since retired from the project. Thanks again for all his work! - + Mailing list - ------------------------ + ------------------------------ There is a mailing list available for users who want to track development and converse with other users and developers. This list is for both FreeBSD and Linux support of the AIC7xxx chipsets. @@ -82,13 +98,150 @@ Send regular messages and replies to: AIC7xxx@FreeBSD.ORG - Command line options - ------------------------ + Boot Command line options + ------------------------------ "aic7xxx=no_reset" - Eliminate the SCSI reset delay during startup. Some SCSI devices need some extra time to reset. + "aic7xxx=reverse_scan" - Have the driver register the SCSI cards in the + reverse of the normal order. This may help those people who have more + than one PCI Adaptec controller force the correct controller to be + scsi0 under linux so that their boot hard drive is also sda under + linux + "aic7xxx=extended" - Force the driver to detect extended drive translation + on your controller. This helps those people who have cards without + a SEEPROM make sure that linux and all other operating systems think + the same way about your hard drives. + "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel + to use the correct IRQ type for your card. This only applies to EISA + based controllers. On these controllers, 0 is for Edge triggered + interrupts, and 1 is for Level triggered interrupts. If you aren't + sure or don't know which IRQ trigger type your EISA card uses, then + let the kernel autodetect the trigger type. + "aic7xxx=verbose" - This option can be used in one of two ways. If you + simply specify aic7xxx=verbose, then the kernel will automatically pick + the default set of verbose messages for you to see. Alternatively, you + can specify the command as "aic7xxx=verbose:0xXXXX" where the X entries + are replaced with hexadecimal digits. This option is a bit field type + option. For a full listing of the available options, search for the + #define VERBOSE_xxxxxx lines in the aic7xxx.c file. If you want verbose + messages, then it is recommended that you simply use the aic7xxx=verbose + variant of this command. + "aic7xxx=7895_irq_hack:x" - This option enables some work around code to + fix a bug in the Tyan Thunder II motherboard BIOS. The BIOS + incorrectly sets the IRQs on the two channels of the 7895 to two + different values even though the motherboard hardware doesn't support + this mode of operation. The valid values for x are: 0 to force + both channels to use the IRQ assigned to Channel A, 1 to force both + channels to use the IRQ assigned to Channel B, and -1 will disable + this horrible abomination of a hack. The default is disabled (-1). + "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable + tagged queueing on specific devices. As of driver version 5.0.6, we + now globally enable tagged queueing by default, but we also disable + tagged queueing on all individual devices by default. In order to + enable tagged queueing for certian devices at boot time, a user may + use this boot param. The driver will then parse this message out + and enable the specific device entries that are present based upon + the value given. The param line is parsed in the following manner: + + { - first instance indicates the start of this parameter values + second instance is the start of entries for a particular + device entry + } - end the entries for a particular host adapter, or end the entire + set of parameter entries + , - move to next entry. Inside of a set of device entries, this + moves us to the next device on the list. Outside of device + entries, this moves us to the next host adapter + . - Same effect as , but is safe to use with insmod. + x - the number to enter into the array at this position. + 0 = Enable tagged queueing on this device and use the default + queue depth + 1-254 = Enable tagged queueing on this device and use this + number as the queue depth + 255 = Disable tagged queueing on this device. + Note: anything above 32 for an actual queue depth is wasteful + and not recommended. + + A few examples of how this can be used: + + tag_info:{{8,12,,0,,255,4}} + This line will only effect the first aic7xxx card registered. It + will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2 + at the default, set id 3 to tagged queueing enabled and use the + default queue depth, id 4 default, id 5 disabled, and id 6 to 4. + Any not specified entries stay at the default value, repeated + commas with no value specified will simply increment to the next id + without changing anything for the missing values. + + tag_info:{{8,8},,{8,8}} + First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their + default. Second adapter stays entirely at default. Third + adapter, id 0 to 8, id 1 to 8, remainder at default (identical to + first adapter). + + tag_info:{,,,{,,,64}} + First, second, and third adapters at default values. Fourth + adapter, id 3 to 64. Notice that leading commas simply increment + what the first number effects, and there are no need for trailing + commas. When you close out an adapter, or the entire entry, + anything not explicitly set stays at the default value. + + A final note on this option. The scanner I used for this isn't + perfect or highly robust. If you mess the line up, the worst that + should happen is that the line will get ignored. If you don't + close out the entire entry with the final bracket, then any other + aic7xxx options after this will get ignored. So, in general, be + sure of what you are entering, and after you have it right, just + add it to the lilo.conf file so there won't be any mistakes. As + a means of checking this parser, the entire tag_info array for + each card is now printed out in the /proc/scsi/aic7xxx/x file. You + can use that to verify that your options were parsed correctly. + + Boot command line options may be combined to form the proper set of options + a user might need. For example, the following is valid: + + aic7xxx=verbose,extended,irq_trigger:1 + + The only requirement is that individual options be separated by a comma on + the command line. + + Module Loading command options + ------------------------------ + When loading the aic7xxx driver as a module, the exact same options are + available to the user. However, the syntax to specify the options changes + slightly. For insmod, you need to wrap the aic7xxx= argument in quotes + and replace all ',' with '.'. So, for example, a valid insmod line + would be: + + insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended' + + This line should result in the *exact* same behaviour as if you typed + it in at the lilo prompt and the driver was compiled into the kernel + instead of being a module. The reason for the single quote is so that + the shell won't try to interpret anything in the line, such as {. + Insmod assumes any options starting with a letter instead of a number + is a character string (which is what we want) and by switching all of + the commas to periods, insmod won't interpret this as more than one + string and write junk into our binary image. I consider it a bug in + the insmod program that even if you wrap your string in quotes (quotes + that pass the shell mind you and that insmod sees) it still treates + a comma inside of those quotes as starting a new variable, resulting + in memory scribbles if you don't switch the commas to periods. + + + Kernel Compile options + ------------------------------ + The various kernel compile time options for this driver are now fairly + well documented in the file Documentation/Configure.help. In order to + see this documentation, you need to use one of the advanced configuration + programs (menuconfig and xconfig). If you are using the "make menuconfig" + method of configuring your kernel, then you would simply highlight the + option in question and hit the F1 key. If you are using the "make xconfig" + method of configuring your kernel, then simply click on the help button next + to the option you have questions about. The help information from the + Configure.help file will then get automatically displayed. /proc support - ------------------------ + ------------------------------ The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/ directory. That directory contains a file for each SCSI controller in the system. Each file presents the current configuration and transfer @@ -98,12 +251,12 @@ Matthew Jacob for statistics support. FTP sites - ------------------------ - ftp://ftp.teleport.com/users/deang/Linux/aic7xxx/ - - Main Linux AIC7xxx driver release/pre-release site - - Experimental/development patches and bootdisks + ------------------------------ ftp://ftp.dialnet.net/pub/linux/aic7xxx/ + - Primary site for Doug Ledford developed driver releases - US Linux mirror of Teleport site + ftp://ftp.pcnet.com/users/eischen/Linux/ + - Dan Eischen's driver distribution area ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/ - European Linux mirror of Teleport site @@ -113,3 +266,4 @@ $Revision: 3.0 $ +Modified by Doug Ledford 1998 diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/README.in2000 linux/drivers/scsi/README.in2000 --- v2.0.33/linux/drivers/scsi/README.in2000 Tue Aug 5 13:26:16 1997 +++ linux/drivers/scsi/README.in2000 Wed Jun 3 15:17:48 1998 @@ -1,4 +1,11 @@ +UPDATE NEWS: version 1.32 - 28 Mar 98 + + Removed the check for legal IN2000 hardware versions: + It appears that the driver works fine with serial + EPROMs (the 8-pin chip that defines hardware rev) as + old as 2.1, so we'll assume that all cards are OK. + UPDATE NEWS: version 1.31 - 6 Jul 97 Fixed a bug that caused incorrect SCSI status bytes to be diff -u --recursive --new-file v2.0.33/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx --- v2.0.33/linux/drivers/scsi/README.ncr53c8xx Thu Aug 14 10:30:08 1997 +++ linux/drivers/scsi/README.ncr53c8xx Wed Jun 3 15:17:48 1998 @@ -4,7 +4,7 @@ 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -19 June 1997 +2 January 1998 =============================================================================== 1. Introduction @@ -30,6 +30,7 @@ 10.3 Advised boot setup commands 10.4 PCI configuration fix-up boot option 10.5 Serial NVRAM support boot option + 10.6 SCSI BUS checking boot option 11. Some constants and flags of the ncr53c8xx.h header file 12. Installation 12.1 Provided files @@ -38,6 +39,8 @@ 14. Known problems 14.1 Tagged commands with Iomega Jaz device 14.2 Device names change when another controller is added + 14.3 Using only 8 bit devices with a WIDE SCSI controller. + 14.4 Possible data corruption during a Memory Write and Invalidate 15. SCSI problem troubleshooting 16. Synchonous transfer negotiation tables 16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers @@ -46,6 +49,9 @@ 17.1 Features 17.2 Symbios NVRAM layout 17.3 Tekram NVRAM layout +18. Support for Big Endian + 18.1 Big Endian CPU + 18.2 NCR chip in Big Endian mode of operations =============================================================================== @@ -83,7 +89,7 @@ driver, configuration parameters and control commands available through the proc SCSI file system read / write operations. -This driver has been tested OK with linux/i386 and Linux/Alpha. +This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC. Latest driver version and patches are available at: @@ -429,7 +435,10 @@ CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n) Answer "y" if you suspect your mother board to not allow memory mapped I/O. - May slow down performance a little. + May slow down performance a little. This option is required by + Linux/PPC and is used no matter what you select here. Linux/PPC + suffers no performance loss with this option since all IO is memory + mapped anyway. CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE (default answer: n) Answer "y" if you are sure that all your SCSI devices that are able to @@ -484,7 +493,9 @@ 10.1 Syntax -Setup commands can be passed to the driver at boot time. +Setup commands can be passed to the driver either at boot time or as a +string variable using 'insmod'. + A boot setup command for the ncr53c8xx driver begins with the driver name "ncr53c8xx=". The kernel syntax parser then expects an optionnal list of integers separated with comma followed by an optionnal list of comma- @@ -496,7 +507,14 @@ - set synchronous negotiation speed to 10 Mega-transfers / second. - set DEBUG_NEGO flag. -For the moment, the integer list of arguments is disgarded by the driver. +Since comma seems not to be allowed when defining a string variable using +'insmod', the driver also accepts as option separator. +The following command will install driver module with the same options as +above. + +insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200" + +For the moment, the integer list of arguments is discarded by the driver. It will be used in the future in order to allow a per controller setup. Each string argument must be specified as "keyword:value". Only lower-case @@ -519,8 +537,12 @@ Special features Only apply to 810A, 825A, 860 and 875 controllers. Have no effect with normal 810 and 825. - specf:y enabled - specf:n disabled + specf:y (or 1) enabled + specf:n (or 0) disabled + specf:3 enabled except Memory Write And Invalidate + The default driver setup is 'specf:3'. As a consequence, option 'specf:y' + must be specified in the boot setup command to enable Memory Write And + Invalidate. Ultra SCSI support Only apply to 860 and 875 controllers. @@ -622,6 +644,7 @@ pcifix: