diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/CREDITS linux.14p2/CREDITS --- linux.vanilla/CREDITS Wed Oct 20 01:12:32 1999 +++ linux.14p2/CREDITS Wed Oct 27 18:57:30 1999 @@ -789,12 +789,12 @@ D: Selection mechanism N: Jochen Hein -E: jochen.hein@delphi.central.de +E: jochen@jochen.org P: 1024/4A27F015 25 72 FB E3 85 9F DE 3B CB 0A DA DA 40 77 05 6C D: National Language Support D: Linux Internationalization Project D: German Localization for Linux and GNU software -S: Frankenstraße +S: Frankenstraße 33 S: 34131 Kassel S: Germany @@ -2122,9 +2122,10 @@ N: Tim Waugh E: tim@cyberelk.demon.co.uk D: Co-architect of the parallel-port sharing system -S: 4 Fox Close -S: Bishopstoke -S: SO50 8NB +S: 34 Bladon Close +S: GUILDFORD +S: Surrey +S: GU1 1TY S: United Kingdom N: Juergen Weigert diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/Documentation/Changes linux.14p2/Documentation/Changes --- linux.vanilla/Documentation/Changes Sat Aug 14 02:27:12 1999 +++ linux.14p2/Documentation/Changes Fri Oct 22 22:44:33 1999 @@ -43,7 +43,7 @@ for all your Linux kernel needs. -Last updated: July 13, 1999 +Last updated: October 13, 1999 Current Author: Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu). @@ -61,19 +61,19 @@ - Linux libc6 C Library 2.0.7pre6 ; ls -l /lib/libc* - Dynamic Linker (ld.so) 1.9.9 ; ldd --version or ldd -v - Linux C++ Library 2.7.2.8 ; ls -l /usr/lib/libg++.so.* -- Procps 1.2.9 ; ps --version +- Procps 2.0.3 ; ps --version - Procinfo 16 ; procinfo -v - Psmisc 17 ; pstree -V - Net-tools 1.52 ; hostname -V - Loadlin 1.6a - Sh-utils 1.16 ; basename --v -- Autofs 3.1.1 ; automount --version +- Autofs 3.1.3 ; automount --version - NFS 2.2beta40 ; showmount --version - Bash 1.14.7 ; bash -version - Ncpfs 2.2.0 ; ncpmount -v -- Pcmcia-cs 3.0.13 ; cardmgr -V -- PPP 2.3.8 ; pppd --version -- Util-linux 2.9t ; chsh -v +- Pcmcia-cs 3.0.14 ; cardmgr -V +- PPP 2.3.10 ; pppd --version +- Util-linux 2.9z ; chsh -v Upgrade notes ************* @@ -226,7 +226,7 @@ Note that the latest compilers (egcs, pgcc, gcc 2.8) may do Bad Things while compiling your kernel, particularly if absurd optimizations (like -O9) are used. Caveat emptor. Currently, the only -C compiler available in a binary distribution is egcs. Version 1.0.3 +C compiler available in a binary distribution is egcs. Version 1.1.2 seems okay; if you have to have a binary, you may be successful using that. In general, however, gcc-2.7.2.3 is known to be stable, while egcs and others have not been as thoroughly tested yet. @@ -284,8 +284,8 @@ ====== As of 2.1.41, the format of /proc/meminfo has changed. This broke -many memory utils, which have to be upgraded. Get the new procps-1.2 -and you should be set. +many memory utils, which have to be upgraded. Get the new procps and +you should be set. Network File System =================== @@ -519,23 +519,15 @@ Binutils ======== -The 2.8.1.0.23 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.8.1.0.23.bin.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.8.1.0.23.bin.tar.gz +The 2.9.1.0.25 release: +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/binutils-2.9.1.0.25.tar.gz Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.8.1.0.23 -ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.8.1.0.23 +ftp://ftp.varesearch.com/pub/support/hjl/binutils/2.9.1/release.binutils-2.9.1.0.25 -The 2.9.1.0.15 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.9.1.0.15.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-glibc.x86.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15-libc5.x86.tar.gz -ftp://metalab.unc.edu/pub/Linux/GCC/binutils-2.9.1.0.15.tar.gz +The 2.9.5.0.16 release: +ftp://ftp.varesearch.com/pub/support/hjl/binutils/binutils-2.9.5.0.16.tar.bz2 Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.9.1.0.15 -ftp://metalab.unc.edu/pub/Linux/GCC/release.binutils-2.9.1.0.15 +ftp://ftp.varesearch.com/pub/support/hjl/binutils/release.binutils-2.9.5.0.16 Bin86 ===== @@ -547,14 +539,12 @@ Gnu C ===== -The egcs-1.0.3 release: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-glibc.x86.tar.bz2 -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/egcs-1.0.3-libc5.x86.tar.bz2 -ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-glibc.x86.tar.bz2 -ftp://metalab.unc.edu/pub/Linux/GCC/egcs-1.0.3-libc5.x86.tar.bz2 +The egcs-1.1.2 release: +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-glibc.x86.tar.bz2 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-libc5.x86.tar.bz2 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/egcs-1.1.2-alpha.x86.tar.bz2 Installation notes: -ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.egcs-1.0.3 -ftp://metalab.unc.edu/pub/Linux/GCC/release.egcs-1.0.3 +ftp://ftp.varesearch.com/pub/support/hjl/gcc/egcs-1.1.2/release.egcs-1.1.2 Gnu C 2.7.2.3 source: ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz @@ -596,15 +586,14 @@ Procps utilities ================ -The 1.2 release: -ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.2.9.tar.gz -ftp://metalab.unc.edu/pub/Linux/system/status/ps/procps-1.2.9.tgz +The 2.0.3 release: +ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-2.0.3.tar.gz Procinfo utilities ================== -The 16 release: -ftp://ftp.cistron.nl/pub/people/svm/procinfo-16.tar.gz +The 17 release: +ftp://ftp.cistron.nl/pub/people/svm/procinfo-17.tar.gz Psmisc utilities ================ @@ -624,7 +613,6 @@ ====== The 0.98.6 release: -ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu-0.98.6.tgz ftp://ftp.dosemu.org/dosemu/dosemu-0.98.6.tgz Loadlin @@ -645,7 +633,7 @@ ========== The 2.9 release: -ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9t.tar.gz +ftp://ftp.win.tue.nl/pub/linux/utils/util-linux/util-linux-2.9z.tar.gz Autofs ====== @@ -660,9 +648,9 @@ ftp://ftp.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz ftp://linux.nrao.edu/mirrors/fb0429.mathematik.th-darmstadt.de/pub/linux/okir/dontuse/nfs-server-2.2beta40.tar.gz -The kernel-level 1.4.4 release: -ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.4.tar.gz -ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.4.tar.gz +The kernel-level 1.4.7 release: +ftp://ftp.varesearch.com/pub/support/hjl/knfsd/knfsd-1.4.7.tar.gz +ftp://ftp.kernel.org/pub/linux/devel/gcc/knfsd-1.4.7.tar.gz Net-tools ========= @@ -707,8 +695,8 @@ Pcmcia-cs ========= -The 3.0.13 release: -ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs.3.0.13.tar.gz +The 3.0.14 release: +ftp://sourceforge.org/pcmcia/pcmcia-cs.3.0.14.tar.gz Setserial ========= @@ -720,15 +708,15 @@ PPP === -The 2.3.8 release: -ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.8.tar.gz +The 2.3.10 release: +ftp://cs.anu.edu.au/pub/software/ppp/ppp-2.3.10.tar.gz IP Chains ========= -The 1.3.8 release: -ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.gz -ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.8.tar.bz2 +The 1.3.9 release: +ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.gz +ftp://ftp.rustcorp.com/ipchains/ipchains-1.3.9.tar.bz2 IP Masq Adm =========== @@ -739,11 +727,11 @@ DHCP clients ============ -The 2.0b1pl27 ISC dhcpclient release: -ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0b1pl27.tar.gz +The 2.0 ISC dhcpclient release: +ftp://ftp.isc.org/isc/dhcp/test/dhcp-2.0.tar.gz -The 1.3.17-pl2 PhysTech dhcpcd release: -ftp://ftp.phystech.com/pub/dhcpcd-1.3.17-pl2.tar.gz +The 1.3.18-pl1 PhysTech dhcpcd release: +ftp://ftp.phystech.com/pub/dhcpcd-1.3.18-pl1.tar.gz iBCS ==== @@ -754,8 +742,8 @@ Asun netatalk ============= -The 2.0a18.2 release: -ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.0a18.2.tar.gz +The 2.1.3 release: +ftp://ftp.u.washington.edu/pub/user-supported/asun/netatalk-1.4b2+asun2.1.3.tar.gz Fbset ===== @@ -792,9 +780,9 @@ IP utils ======== -The June 1999 release: -ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990630.tar.gz -ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990610.tar.gz +The latest releases: +ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss990824.tar.gz +ftp://ftp.inr.ac.ru/ip-routing/iputils-ss990915.tar.gz Patch ===== diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/Documentation/Configure.help linux.14p2/Documentation/Configure.help --- linux.vanilla/Documentation/Configure.help Wed Oct 20 01:12:32 1999 +++ linux.14p2/Documentation/Configure.help Fri Oct 22 22:52:53 1999 @@ -9422,20 +9422,6 @@ You can say M here to compile this driver as a module; the module is called sb.o. -Generic OPL2/OPL3 FM synthesizer support -CONFIG_SOUND_ADLIB - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. - - If unsure, say Y. - - #Loopback MIDI device support #CONFIG_SOUND_VMIDI ### @@ -9715,7 +9701,15 @@ FM synthesizer (YM3812/OPL-3) support CONFIG_SOUND_YM3812 - Answer Y here, unless you know you will not need the option. + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). + Please read the file Documentation/sound/OPL3 if your card has an + OPL3 chip. + + If unsure, say Y. Sun Audio support CONFIG_SUN_AUDIO diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/Documentation/kernel-docs.txt linux.14p2/Documentation/kernel-docs.txt --- linux.vanilla/Documentation/kernel-docs.txt Sat Aug 14 02:26:08 1999 +++ linux.14p2/Documentation/kernel-docs.txt Wed Oct 27 18:53:51 1999 @@ -1,15 +1,14 @@ - - INDEX OF DOCUMENTATION FOR PEOPLE INTERESTED IN WRITING AND/OR UNDERSTANDING - THE LINUX KERNEL. - - Juan-Mariano de Goyeneche - - - /* - * The latest version of this document may be found at: - * http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html - */ + Index of Documentation for People Interested in Writing and/or + + Understanding the Linux Kernel. + + Juan-Mariano de Goyeneche < jmseyas@dit.upm.es> + +/* + * The latest version of this document may be found at: + * http://www.dit.upm.es/~jmseyas/linux/kernel/hackers-docs.html + */ The need for a document like this one became apparent in the linux-kernel mailing list as the same questions, asking for pointers @@ -31,301 +30,411 @@ corrections, ideas or comments are also welcomed. The papers that follow are listed in no particular order. All are - catalogued with the following fields: the document's "Title", the - "Author"/s, the "URL" where they can be found, some "Keywords" - helpful when searching for specific topics, and a brief "Description" - of the Document. + cataloged with the following fields: the document's "Title", the + "Author"/s, the "URL" where they can be found, some "Keywords" helpful + when searching for specific topics, and a brief "Description" of the + Document. Enjoy! - - ON-LINE DOCS: - - + Title: "The Linux Kernel" - Author: David A. Rusling. - URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html - Keywords: everything!, book. - Description: On line, 200 pages book describing most - aspects of the Linux Kernel. Probably, the first reference - for beginners. Lots of illustrations explaining data - structures use and relationships in the purest Richard W. - Stevens' style. Contents: "1.-Hardware Basics, 2.-Software - Basics, 3.-Memory Management, 4.-Processes, 5.-Interprocess - Communication Mechanisms, 6.-PCI, 7.-Interrupts and Interrupt - Handling, 8.-Device Drivers, 9.-The File system, - 10.-Networks, 11.-Kernel Mechanisms, 12.-Modules, 13.-The - Linux Kernel Sources, A.-Linux Data Structures, B.-The Alpha - AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU - General Public License, Glossary". In short: a must have. - - + Title: "The Linux Kernel Hackers' Guide" - Author: Michael K.Johnson and others. - URL: http://www.redhat.com:8080/HyperNews/get/khg.html - Keywords: everything! - Description: No more Postscript book-like version. Only - HTML now. Many people have contributed. The interface is - similar to web available mailing lists archives. You can find - some articles and then some mails asking questions about them - and/or complementing previous contributions. A little bit - anarchic in this aspect, but with some valuable information - in some cases. - - + Title: "Tour Of the Linux Kernel Source" - Author: Vijo Cherian. - URL: http://www.svrec.ernet.in/~vijo/tolks/tolks.html - Keywords: - Description: The name says it all. A tour of the sources, - describing directories, files, variables, data structures... - It covers general stuff, device drivers, filesystems, IPC and - Network Code. - - + Title: "Overview of the Virtual File System" - Author: Richard Gooch. - URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt - Keywords: VFS, File System, mounting filesystems, opening - files, dentries, - dcache. Description: Brief introduction to the Linux - Virtual File System. What is it, how it works, operations - taken when opening a file or mounting a file system and - description of important data structures explaining the - purpose of each of their entries. - - + Title: "The Linux RAID-1, 4, 5 Code" - Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza. - URL: http://www.ssc.com/lj/issue44/2391.html - Keywords: RAID, MD driver. - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "A description of the implementation of the - RAID-1, RAID-4 and RAID-5 personalities of the MD device - driver in the Linux kernel, providing users with high - performance and reliable, secondary-storage capability using - software". - - + Title: "Dynamic Kernels: Modularized Device Drivers" - Author: Alessandro Rubini. - URL: http://www.ssc.com/lj/issue23/1219.html - Keywords: device driver, module, loading/unloading modules, - allocating - resources. Description: Linux Journal Kernel Korner - article. Here is it's abstract: "This is the first of a - series of four articles co-authored by Alessandro Rubini and - Georg Zezchwitz which present a practical approach to writing - Linux device drivers as kernel loadable modules. This - installment presents an introduction to the topic, preparing - the reader to understand next month's installment". - - + Title: "Dynamic Kernels: Discovery" - Author: Alessandro Rubini. - URL: http://www.ssc.com/lj/issue24/kk24.html - Keywords: character driver, init_module, clean_up module, - autodetection, - mayor number, minor number, file operations, open(), close(). - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "This article, the second of four, introduces - part of the actual code to create custom module implementing - a character device driver. It describes the code for module - initialization and cleanup, as well as the open() and close() - system calls". - - + Title: "The Devil's in the Details" - Author: Georg v. Zezschwitz and Alessandro Rubini. - URL: http://www.ssc.com/lj/issue25/kk25.html - Keywords: read(), write(), select(), ioctl(), blocking/non - blocking mode, - interrupt handler. Description: Linux Journal Kernel Korner - article. Here is it's abstract: "This article, the third of - four on writing character device drivers, introduces concepts - of reading, writing, and using ioctl-calls". - - + Title: "Dissecting Interrupts and Browsing DMA" - Author: Alessandro Rubini and Georg v. Zezschwitz. - URL: http://www.ssc.com/lj/issue26/interrupt.html - Keywords: interrupts, irqs, DMA, bottom halves, task - queues. - Description: Linux Journal Kernel Korner article. Here is - it's abstract: "This is the fourth in a series of articles - about writing character device drivers as loadable kernel - modules. This month, we further investigate the field of - interrupt handling. Though it is conceptually simple, - practical limitations and constraints make this an - ``interesting'' part of device driver writing, and several - different facilities have been provided for different - situations. We also investigate the complex topic of DMA". - - + Title: "Network Buffers And Memory Management" - Author: Alan Cox. - URL: http://www.ssc.com/lj/issue30/kk30.html - Keywords: sk_buffs, network devices, protocol/link layer - variables, network - devices flags, transmit, receive, configuration, multicast. - Description: Linux Journal Kernel Korner. Here is the - abstract: "Writing a network device driver for Linux is - fundamentally simple---most of the complexity (other than - talking to the hardware) involves managing network packets in - memory". - - + Title: "An Introduction to the Linux 1.3.x Networking Code" - Author: Vipul Gupta. - URL: - http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html - Keywords: files, sk_buffs. - Description: A short description of files under the net/ - directory. Each file has a one- or two-line paragraph to - describe it. Also, sk_buffs is explained with some - beautiful pictures. A little bit outdated. - - + Title: "Linux ioctl() Primer" - Author: Vipul Gupta. - URL: - http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html - Keywords: ioctl, socket. - Description: Little description and examples on the use and - implementation of the ioctl() system call. A little bit - biased towards sockets. - - + Title: "Writing Linux Device Drivers" - Author: Michael K. Johnson. - URL: http://www.redhat.com/~johnsonm/devices.html - Keywords: files, VFS, file operations, kernel interface, - character vs - block devices, I/O access, hardware interrupts, DMA, access - to user memory, memory allocation, timers. Description: - Introductory 50-minutes (sic) tutorial on writing device - drivers. 12 pages written by the same author of the "Kernel - Hackers' Guide" which give a very good overview of the topic. - - + Title: "The Venus kernel interface" - Author: Peter J. Braam. - URL: - http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html - Keywords: coda, filesystem, venus, cache manager. - Description: "This document describes the communication - between Venus and kernel level file system code needed for - the operation of the Coda filesystem. This version document - is meant to describe the current interface (version 1.0) as - well as improvements we envisage". - - + Title: "Programming PCI-Devices under Linux" - Author: Claus Schroeter. - URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pc - ip.ps.gz - Keywords: PCI, device, busmastering. - Description: 6 pages tutorial on PCI programming under - Linux. Gives the basic concepts on the architecture of the - PCI subsystem, as long as basic functions and macros to - read/write the devices and perform busmastering. - - + Title: "Writing Character Device Driver for Linux" - Author: R. Baruch and C. Schroeter. - URL: - ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/dr - ivers.ps.gz - Keywords: character device drivers, I/O, signals, DMA, - accessing ports in user space, kernel environment. - Description: 68 pages paper on writing character drivers. A - little bit old (1.993, 1.994) although still useful. - - - - * BOOKS: (Not on-line) - - + Title: "Linux Device Drivers" - Author: Alessandro Rubini. - Publisher: O'Reilly &Associates. - Date: 1998. - ISBN: 1-56592-292-1 - - + Title: "Linux Kernel Internals" - Author: Michael Beck. - Publisher: Addison-Wesley. - Date: 1997. - ISBN: 0-201-33143-8 (second edition) - - + Title: "The Design of the UNIX Operating System" - Author: Maurice J. Bach. - Publisher: Prentice Hall. - Date: 1986. - ISBN: ??? - - + Title: "The Design and Implementation of the 4.3 BSD UNIX - Operating System" - Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael - J. Karels, John S. Quarterman. - Publisher: Addison-Wesley. - Date: 1989 (reprinted with corrections on October, 1990). - ISBN: 0-201-06196-1 - - + Title: "The Design and Implementation of the 4.4 BSD UNIX - Operating System" - Author: Marshall Kirk McKusick, Keith Bostic, Michael J. - Karels, John S. Quarterman. - Publisher: Addison-Wesley. - Date: 1996. - ISBN: 0-201-54979-4 - - + Title: "Programmation Linux 2.0 API systeme et - fonctionnement du noyau" - Author: Remy Card, Eric Dumas, Franck Mevel. - Publisher: Eyrolles. - Date: 1997. - Pages: 520. ISBN: 2-212-08932-5 - - + Title: "Unix internals -- the new frontiers" - Author: Uresh Vahalia. - Publisher: Prentice Hall. - Date: 1996. - Pages: 600. ISBN: 0-13-101908-2 - - - * MISCELLANEOUS: + ON-LINE DOCS: - + Name: Linux Source Driver. - URL: http://lsd.linux.cz - Keywords: Browsing. - Description: "Linux Source Driver (LSD) is an application, - which can make browsing source codes of Linux kernel easier - than you can imagine. You can select between multiple - versions of kernel (e.g. 0.01, 1.0.0, 2.0.33, 2.0.34pre13, - 2.0.0, 2.1.101 etc.). With LSD you can search Linux kernel - (fulltext, macros, types, functions and variables) and LSD - can generate patches for you on the fly (files, directories - or kernel)". - - + Name: Linux Weekly News. - URL: http://lwn.net - Keywords: last kernel news. - Description: The title says it all. There's a fixed kernel - section summarizing developers' work, bug fixes, new features - and versions produced during the week. Published every - Thursday. - - + Name: CuTTiNG.eDGe.LiNuX. - URL: http://edge.linuxhq.com - Keywords: changelist. - Description: Site which provides the changelist for every - kernel release. What's new, what's better, what's changed. - Myrdraal reads the patchs and describes them. Pointers to the - patches are there, too. + * Title: "The Linux Kernel" + Author: David A. Rusling. + URL: http://sunsite.unc.edu/linux/LDP/tlk/tlk.html + Keywords: everything!, book. + Description: On line, 200 pages book describing most aspects of + the Linux Kernel. Probably, the first reference for beginners. + Lots of illustrations explaining data structures use and + relationships in the purest Richard W. Stevens' style. Contents: + "1.-Hardware Basics, 2.-Software Basics, 3.-Memory Management, + 4.-Processes, 5.-Interprocess Communication Mechanisms, 6.-PCI, + 7.-Interrupts and Interrupt Handling, 8.-Device Drivers, 9.-The + File system, 10.-Networks, 11.-Kernel Mechanisms, 12.-Modules, + 13.-The Linux Kernel Sources, A.-Linux Data Structures, B.-The + Alpha AXP Processor, C.-Useful Web and FTP Sites, D.-The GNU + General Public License, Glossary". In short: a must have. + + * Title: "The Linux Kernel Hackers' Guide" + Author: Michael K.Johnson and others. + URL: http://khg.redhat.com/HyperNews/get/khg.html + Keywords: everything! + Description: No more Postscript book-like version. Only HTML now. + Many people have contributed. The interface is similar to web + available mailing lists archives. You can find some articles and + then some mails asking questions about them and/or complementing + previous contributions. A little bit anarchic in this aspect, but + with some valuable information in some cases. + + * Title: "Conceptual Architecture of the Linux Kernel" + Author: Ivan T. Bowman. + URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a1.html + Keywords: conceptual software arquitecture, extracted design, + reverse engineering, system structure. + Description: Conceptual software arquitecture of the Linux kernel, + automatically extracted from the source code. Very detailed. Good + figures. Gives good overall kernel understanding. + + * Title: "Concrete Architecture of the Linux Kernel" + Author: Ivan T. Bowman, Saheem Siddiqi, and Meyer C. Tanuan. + URL: http://plg.uwaterloo.ca/~itbowman/papers/CS746G-a2.html + Keywords: concrete arquitecture, extracted design, reverse + engineering, system structure, dependencies. + Description: Concrete arquitecture of the Linux kernel, + automatically extracted from the source code. Very detailed. Good + figures. Gives good overall kernel understanding. This papers + focus on lower details than its predecessor (files, variables...). + + * Title: "Linux as a Case Study: Its Extracted Software + Architecture" + Author: Ivan T. Bowman, Richard C. Holt and Neil V. Brewster. + URL: http://plg.uwaterloo.ca/~itbowman/papers/linuxcase.html + Keywords: software architecture, architecture recovery, + redocumentation. + Description: Paper appeared at ICSE'99, Los Angeles, May 16-22, + 1999. A mixture of the previous two documents from the same + author. + + * Title: "Overview of the Virtual File System" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/vfs.txt + Keywords: VFS, File System, mounting filesystems, opening files, + dentries, + dcache. Description: Brief introduction to the Linux Virtual File + System. What is it, how it works, operations taken when opening a + file or mounting a file system and description of important data + structures explaining the purpose of each of their entries. + + * Title: "The Linux RAID-1, 4, 5 Code" + Author: Ingo Molnar, Gadi Oxman and Miguel de Icaza. + URL: http://www.ssc.com/lj/issue44/2391.html + Keywords: RAID, MD driver. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "A description of the implementation of the RAID-1, + RAID-4 and RAID-5 personalities of the MD device driver in the + Linux kernel, providing users with high performance and reliable, + secondary-storage capability using software". + + * Title: "Dynamic Kernels: Modularized Device Drivers" + Author: Alessandro Rubini. + URL: http://www.ssc.com/lj/issue23/1219.html + Keywords: device driver, module, loading/unloading modules, + allocating resources. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This is the first of a series of four articles + co-authored by Alessandro Rubini and Georg Zezchwitz which present + a practical approach to writing Linux device drivers as kernel + loadable modules. This installment presents an introduction to the + topic, preparing the reader to understand next month's + installment". + + * Title: "Dynamic Kernels: Discovery" + Author: Alessandro Rubini. + URL: http://www.ssc.com/lj/issue24/kk24.html + Keywords: character driver, init_module, clean_up module, + autodetection, + mayor number, minor number, file operations, open(), close(). + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This article, the second of four, introduces part of + the actual code to create custom module implementing a character + device driver. It describes the code for module initialization and + cleanup, as well as the open() and close() system calls". + + * Title: "The Devil's in the Details" + Author: Georg v. Zezschwitz and Alessandro Rubini. + URL: http://www.ssc.com/lj/issue25/kk25.html + Keywords: read(), write(), select(), ioctl(), blocking/non + blocking mode, interrupt handler. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This article, the third of four on writing character + device drivers, introduces concepts of reading, writing, and using + ioctl-calls". + + * Title: "Dissecting Interrupts and Browsing DMA" + Author: Alessandro Rubini and Georg v. Zezschwitz. + URL: http://www.ssc.com/lj/issue26/interrupt.html + Keywords: interrupts, irqs, DMA, bottom halves, task queues. + Description: Linux Journal Kernel Korner article. Here is it's + abstract: "This is the fourth in a series of articles about + writing character device drivers as loadable kernel modules. This + month, we further investigate the field of interrupt handling. + Though it is conceptually simple, practical limitations and + constraints make this an ``interesting'' part of device driver + writing, and several different facilities have been provided for + different situations. We also investigate the complex topic of + DMA". + + * Title: "Network Buffers And Memory Management" + Author: Alan Cox. + URL: http://www.ssc.com/lj/issue30/kk30.html + Keywords: sk_buffs, network devices, protocol/link layer + variables, network devices flags, transmit, receive, + configuration, multicast. + Description: Linux Journal Kernel Korner. Here is the abstract: + "Writing a network device driver for Linux is fundamentally + simple---most of the complexity (other than talking to the + hardware) involves managing network packets in memory". + + * Title: "An Introduction to the Linux 1.3.x Networking Code" + Author: Vipul Gupta. + URL: http://anchor.cs.binghamton.edu/courses/cs628/linux-net.html + Keywords: files, sk_buffs. + Description: A short description of files under the net/ + directory. Each file has a one or two lines paragraph description. + sk_buffs explained, too, with some beautiful pictures. A little + bit outdated. + + * Title: "Linux ioctl() Primer" + Author: Vipul Gupta. + URL: http://anchor.cs.binghamton.edu/courses/cs628/ioctl.html + Keywords: ioctl, socket. + Description: Little description and examples on the use and + implementation of the ioctl() system call. A little bit biased + towards sockets. + + * Title: "Writing Linux Device Drivers" + Author: Michael K. Johnson. + URL: http://www.redhat.com/~johnsonm/devices.html + Keywords: files, VFS, file operations, kernel interface, character + vs block devices, I/O access, hardware interrupts, DMA, access to + user memory, memory allocation, timers. + Description: Introductory 50-minutes (sic) tutorial on writing + device drivers. 12 pages written by the same author of the "Kernel + Hackers' Guide" which give a very good overview of the topic. + + * Title: "The Venus kernel interface" + Author: Peter J. Braam. + URL: + http://www.coda.cs.cmu.edu/doc/html/kernel-venus-protocol.html + Keywords: coda, filesystem, venus, cache manager. + Description: "This document describes the communication between + Venus and kernel level file system code needed for the operation + of the Coda filesystem. This version document is meant to describe + the current interface (version 1.0) as well as improvements we + envisage". + + * Title: "Programming PCI-Devices under Linux" + Author: Claus Schroeter. + URL: + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/pcip.ps + .gz + Keywords: PCI, device, busmastering. + Description: 6 pages tutorial on PCI programming under Linux. + Gives the basic concepts on the architecture of the PCI subsystem, + as long as basic functions and macros to read/write the devices + and perform busmastering. + + * Title: "Writing Character Device Driver for Linux" + Author: R. Baruch and C. Schroeter. + URL: + ftp://ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers/drivers + .ps.gz + Keywords: character device drivers, I/O, signals, DMA, accesing + ports in user space, kernel environment. + Description: 68 pages paper on writing character drivers. A little + bit old (1.993, 1.994) although still useful. + + * Title: "The Second Extended Filesystem" + Author: Matthew Wilcox. + URL: http://pocket.fluff.org/~mrw/linux/ext2.txt + Keywords: ext2, filesystem. + Description: Description of ext2's blocks, directories, inodes ... + + * Title: "Analysis of the Ext2fs structure" + Author: Louis-Dominique Dubeau. + URL: http://step.polymtl.ca/~ldd/ext2fs/ext2fs_toc.html + Keywords: ext2, filesystem, ext2fs. + Description: Description of ext2's blocks, directories, inodes, + bitmaps, invariants ... + + * Title: "Kernel API changes from 2.0 to 2.2" + Author: Richard Gooch. + URL: + http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html + Keywords: 2.2, changes. + Description: Kernel functions/structures/variables which changed + from 2.0.x to 2.2.x. + + * Title: "Kernel API changes from 2.2 to 2.3" + Author: Richard Gooch. + URL: + http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html + Keywords: 2.3, changes. + Description: Kernel functions/structures/variables which changed + from 2.2.x to 2.3.x. + + * Title: "Linux Kernel Module Programming Guide" + Author: Ori Pomerantz. + URL: + http://www.leo.org/pub/comp/os/unix/linux/sunsite/docs/linux-doc-p + roject/module-programming-guide/index.html + Keywords: modules, free book, /proc, ioctls, system calls, + interrupt handlers . + Description: Very nice 92 pages free book on the topic of modules + programming. Lots of examples. + + * Title: "Device File System (devfs) Overview" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.txt + Keywords: filesystem, /dev, devfs, dynamic devices, major/minor + allocation, device management. + Description: Document describing Richard Gooch's controversial + devfs, which allows for dynamic devices, only shows present + devices in /dev, gets rid of major/minor numbers allocation + problems, and allows for hundreds of identical devices (which some + USB systems might demand soon). + + * Title: "I/O Event Handling Under Linux" + Author: Richard Gooch. + URL: http://www.atnf.csiro.au/~rgooch/linux/docs/io-events.html + Keywords: IO, I/O, select(2), poll(2), FDs, aio_read(2), readiness + event queues. + Description: From the Introduction: "I/O Event handling is about + how your Operating System allows you to manage a large number of + open files (file descriptors in UNIX/POSIX, or FDs) in your + application. You want the OS to notify you when FDs become active + (have data ready to be read or are ready for writing). Ideally you + want a mechanism that is scalable. This means a large number of + inactive FDs cost very little in memory and CPU time to manage". + + BOOKS: (Not on-line) + + * Title: "Linux Device Drivers" + Author: Alessandro Rubini. + Publisher: O'Reilly &Associates. + Date: 1998. + ISBN: 1-56592-292-1 + + * Title: "Linux Kernel Internals" + Author: Michael Beck. + Publisher: Addison-Wesley. + Date: 1997. + ISBN: 0-201-33143-8 (second edition) + + * Title: "The Design of the UNIX Operating System" + Author: Maurice J. Bach. + Publisher: Prentice Hall. + Date: 1986. + Pages: 471. + ISBN: 0-13-201757-1 + + * Title: "The Design and Implementation of the 4.3 BSD UNIX + Operating System" + Author: Samuel J. Leffler, Marshall Kirk McKusick, Michael J. + Karels, John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1989 (reprinted with corrections on October, 1990). + ISBN: 0-201-06196-1 + + * Title: "The Design and Implementation of the 4.4 BSD UNIX + Operating System" + Author: Marshall Kirk McKusick, Keith Bostic, Michael J. Karels, + John S. Quarterman. + Publisher: Addison-Wesley. + Date: 1996. + ISBN: 0-201-54979-4 + + * Title: "Programmation Linux 2.0 API systeme et fonctionnement du + noyau" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: Eyrolles. + Date: 1997. + Pages: 520. + ISBN: 2-212-08932-5 + Notes: French. + + * Title: "The Linux Kernel Book" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: John Wiley & Sons. + Date: 1998. + ISBN: 0-471-98141-9 + Notes: English translation. + + * Title: "Linux 2.0" + Author: Remy Card, Eric Dumas, Franck Mevel. + Publisher: Gestión 2000. + Date: 1997. + Pages: 501. + ISBN: 8-480-88208-5 + Notes: Spanish translation. + + * Title: "Unix internals -- the new frontiers" + Author: Uresh Vahalia. + Publisher: Prentice Hall. + Date: 1996. + Pages: 600. + ISBN: 0-13-101908-2 + + * Title: "Linux Core Kernel Commentary. Guide to Insider's Knowledge + on the Core Kernel od the Linux Code" + Author: Scott Maxwell. + Publisher: ???. + Date: 1999. + Pages: 592. + ISBN: 1-57610-469-9 + Notes: CD-ROM included. + + MISCELLANEOUS: + + * Name: Linux Source Driver. + URL: http://lsd.linux.cz + Keywords: Browsing source code. + Description: "Linux Source Driver (LSD) is an application, which + can make browsing source codes of Linux kernel easier than you can + imagine. You can select between multiple versions of kernel (e.g. + 0.01, 1.0.0, 2.0.33, 2.0.34pre13, 2.0.0, 2.1.101 etc.). With LSD + you can search Linux kernel (fulltext, macros, types, functions + and variables) and LSD can generate patches for you on the fly + (files, directories or kernel)". + + * Name: Cross-Referencing Linux. + URL: http://lxr.linux.no/source/ + Keywords: Browsing source code. + Description: Another web-based Linux kernel source code browser. + Lots of cross references to variables and functions. You can see + where they are defined and where they are used. + + * Name: Linux Weekly News. + URL: http://lwn.net + Keywords: latest kernel news. + Description: The title says it all. There's a fixed kernel section + summarizing developers' work, bug fixes, new features and versions + produced during the week. Published every Thursday. + + * Name: Kernel Traffic. + URL: http://lwn.net + Keywords: linux-kernel mailing list, weekly kernel news. + Description: Weekly newsletter covering the most relevant + discussions of the linux-kernel mailing list. + + * Name: CuTTiNG.eDGe.LiNuX. + URL: http://edge.kernelnotes.org + Keywords: changelist. + Description: Site which provides the changelist for every kernel + release. What's new, what's better, what's changed. Myrdraal reads + the patches and describes them. Pointers to the patches are there, + too. + + * Name: New linux-kernel Mailing List FAQ. + URL: Original site: + http://www.altern.org/andrebalsa/doc/lkml-faq.html + URL: U.S. mirror site: + http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html + Keywords: linux-kernel mailing list FAQ. + Description: linux-kernel is a mailing list for developers to + communicate. This FAQ builds on the previous linux-kernel mailing + list FAQ maintained by Frohwalt Egerer, who no longer maintains + it. Read it to see how to join the mailing list. Dozens of + interesting questions regarding the list, Linux, developers (who + is ...?), terms (what is...?) are answered here too. Just read it. + + * Name: "Linux Virtual File System" + Author: Peter J. Braam. + URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs + Keywords: slides, VFS, inode, superblock, dentry, dcache. + Description: Set of slides, presumably from a presentation on the + Linux VFS layer. Covers version 2.1.x, with dentries and the + dcache. + _________________________________________________________________ - + Name: New linux-kernel Mailing List FAQ. - URL: Original site: - http://www.altern.org/andrebalsa/doc/lkml-faq.html - URL: U.S. mirror site: - http://www.ececs.uc.edu/~rreilova/linux/lkml-faq.html - Keywords: linux-kernel mailing list FAQ. - Description: linux-kernel is a mailing list for developers - to communicate. This FAQ builds on the previous linux-kernel - mailing list FAQ maintained by Frohwalt Egerer, who no longer - maintains it. Read it to see how to join the mailing list. - Dozens of interesting questions regarding the list, Linux, - developers (who is ...?), terms (what is...?) are answered - here too. Just read it. - - + Name: "Linux Virtual File System" - Author: Peter J. Braam. - URL: http://www.coda.cs.cmu.edu/doc/talks/linuxvfs - Keywords: slides, VFS, inode, superblock, dentry, dcache. - Description: Set of slides, presumably from a presentation - on the Linux VFS layer. Covers version 2.1.x, with dentries - and the dcache. + Document last updated on Wed Oct 27 17:14:03 CEST 1999 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/Documentation/kernel-parameters.txt linux.14p2/Documentation/kernel-parameters.txt --- linux.vanilla/Documentation/kernel-parameters.txt Sat Aug 14 02:26:45 1999 +++ linux.14p2/Documentation/kernel-parameters.txt Fri Oct 22 22:38:10 1999 @@ -50,7 +50,7 @@ it will appear as a kernel argument readable via /proc/cmdline by programs running once the system is up. - 53c7xx= [HW,SCSI] + 53c7xx= [HW,SCSI] Amiga SCSI controllers adb_buttons= [HW,MOUSE] @@ -68,6 +68,8 @@ arcrimi= [HW,NET] + ataflop= [HW, M68k] + atamouse= [HW,MOUSE] Atari Mouse. atascsi= [HW,SCSI] Atari SCSI. @@ -96,22 +98,22 @@ com90xx= [HW,NET] - console= + console= [KNL] output console + comm spec (speed, control, parity) cyclades= [HW,SERIAL] Cyclades multi-serial port adapter. - debug [KNL] Enable kernel debugging. + debug [KNL] Enable kernel debugging (events log level). decnet= [HW,NET] - digi= [HW,SERIAL] + digi= [HW,SERIAL] io parameters + enable/disable command digiepca= [HW,SERIAL] dmascc= [HW,AX25,SERIAL] AX.25 Z80SCC driver with DMA support available. - dmasound= [HW,SOUND] + dmasound= [HW,SOUND] (sound subsystem buffers) dtc3181e= [HW,SCSI] @@ -121,7 +123,7 @@ edb= [HW,PS2] - ether= [HW,NET] Ethernet. + ether= [HW,NET] Ethernet cards parameters (iomem,irq,dev_name). fd_mcs= [HW,SCSI] @@ -129,7 +131,7 @@ floppy= [HW] - ftape= [HW] Floppy Tape subsystem. + ftape= [HW] Floppy Tape subsystem debugging options. gdth= [HW,SCSI] @@ -137,7 +139,8 @@ gvp11= [HW,SCSI] - hd= [EIDE] IDE and EIDE hard drive subsystem. + hd= [EIDE] (E)IDE hard drive subsystem + geometry (Cyl/heads/sectors) or tune parameters. hfmodem= [HW,AX25] @@ -149,10 +152,18 @@ icn= [HW,ISDN] + ide?= [HW] (E)IDE subsystem : config (iomem/irq), tuning or + debugging (serialize,reset,no{dma,tune,probe}) or + chipset specific parameters + + idebus= [HW] (E)IDE subsystem : VLB/PCI bus speed + in2000= [HW,SCSI] init= [KNL] + initrd= [KNL] initial ramdisk path + ip= [PNP] isp16= [HW,CD] @@ -185,7 +196,7 @@ kbd-reset [VT] - load_ramdisk= [RAM] + load_ramdisk= [RAM] initrd loading boolean lp= [LPT] Parallel Printer. @@ -204,7 +215,7 @@ mcdx= [HW,CD] - md= [HW] + md= [HW] RAID subsystems devices and level mdacon= [MDA] @@ -224,6 +235,8 @@ nfsroot= [NFS] + nmi_watchdog= [KNL, BUGS=ix86] debugging features for SMP kernels + no387 [BUGS=ix86] Tells the kernel to use the 387 maths emulation library even if a 387 maths coprocessor is present. @@ -231,6 +244,10 @@ noapic [SMP,APIC] Tells the kernel not to make use of any APIC that may be present on the system. + noasync [HW, M68K] Disables async and sync negotiation for all devices. + + nodisconnect [HW,SCSI, M68K] Disables SCSI disconnects. + no-halt [BUGS=ix86] noinitrd [RAM] Tells the kernel not to load any configured @@ -240,6 +257,8 @@ nosmp [SMP] Tells an SMP kernel to act as a UP kernel. + nosync [HW, M68K] Disables sync negotiation for all devices. + optcd= [HW,CD] panic= @@ -260,7 +279,7 @@ pg. [PARIDE] - pirq= [SMP,APIC] + pirq= [SMP,APIC] mp-table plip= [LP,NET] Parallel port network link. @@ -275,13 +294,14 @@ ramdisk_size= [RAM] - ramdisk_start= [RAM] + ramdisk_start= [RAM] offset of the initrd image when cohabiting with + a kernel image on a floppy reboot= [BUGS=ix86] reserve= - riscom8= [HW,SERIAL] + riscom8= [HW,SERIAL] multi-port serial driver (io parameters) ro [KNL] Mount root device read-only on boot. @@ -303,12 +323,14 @@ specialix= [HW,SERIAL] Specialix multi-serial port adapter. - st= [HW] + st= [HW] SCSI tape parameters (buffers, ..) st0x= [HW,SCSI] stram_swap= [HW] + switches= [HW, M68K] + sym53c416= [HW,SCSI] sym53c8xx= [HW,SCSI] @@ -331,6 +353,6 @@ wdt= [HW] - xd= [HW,XT] + xd= [HW,XT] Original XT 8bit disk controllers xd_geo= [HW,XT] diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/Makefile linux.14p2/Makefile --- linux.vanilla/Makefile Wed Oct 20 01:12:33 1999 +++ linux.14p2/Makefile Fri Oct 22 22:16:43 1999 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 13 -EXTRAVERSION = +SUBLEVEL = 14 +EXTRAVERSION = pre1 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -381,6 +381,10 @@ rm -f drivers/net/hamradio/soundmodem/gentbl rm -f drivers/char/hfmodem/gentbl drivers/char/hfmodem/tables.h rm -f drivers/sound/*_boot.h drivers/sound/.*.boot + rm -f drivers/sound/msndinit.c + rm -f drivers/sound/msndperm.c + rm -f drivers/sound/pndsperm.c + rm -f drivers/sound/pndspini.c rm -f .version .config* config.in config.old rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp rm -f scripts/lxdialog/*.o scripts/lxdialog/lxdialog diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/kernel/process.c linux.14p2/arch/alpha/kernel/process.c --- linux.vanilla/arch/alpha/kernel/process.c Sat Aug 28 20:00:53 1999 +++ linux.14p2/arch/alpha/kernel/process.c Fri Oct 22 21:20:22 1999 @@ -117,58 +117,44 @@ } } -void -generic_kill_arch (int mode, char *restart_cmd) -{ - /* The following currently only has any effect on SRM. We should - fix MILO to understand it. Should be pretty easy. Also we can - support RESTART2 via the ipc_buffer machinations pictured below, - which SRM ignores. */ - - if (alpha_using_srm) { - struct percpu_struct *cpup; - unsigned long flags; - - cpup = (struct percpu_struct *) - ((unsigned long)hwrpb + hwrpb->processor_offset); - - flags = cpup->flags; - - /* Clear reason to "default"; clear "bootstrap in progress". */ - flags &= ~0x00ff0001UL; - - if (mode == LINUX_REBOOT_CMD_RESTART) { - if (!restart_cmd) { - flags |= 0x00020000UL; /* "cold bootstrap" */ - cpup->ipc_buffer[0] = 0; - } else { - flags |= 0x00030000UL; /* "warm bootstrap" */ - strncpy((char *)cpup->ipc_buffer, restart_cmd, - sizeof(cpup->ipc_buffer)); - } - } else { - flags |= 0x00040000UL; /* "remain halted" */ - } - - cpup->flags = flags; - mb(); - - reset_for_srm(); - set_hae(srm_hae); +struct halt_info { + int mode; + char * restart_cmd; +}; -#ifdef CONFIG_DUMMY_CONSOLE - /* This has the effect of reseting the VGA video origin. */ - take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); -#endif +static void +halt_processor(void * generic_ptr) +{ + struct percpu_struct * cpup; + struct halt_info * how = (struct halt_info *)generic_ptr; + unsigned long *flags; + int cpuid = smp_processor_id(); + + /* No point in taking interrupts anymore. */ + __cli(); + + cpup = (struct percpu_struct *) + ((unsigned long)hwrpb + hwrpb->processor_offset + + hwrpb->processor_size * cpuid); + flags = &cpup->flags; + + /* Clear reason to "default"; clear "bootstrap in progress". */ + *flags &= ~0x00ff0001UL; + +#ifdef __SMP__ + /* Secondaries halt here. */ + if (cpuid != smp_boot_cpuid) { + *flags |= 0x00040000UL; /* "remain halted" */ + clear_bit(cpuid, &cpu_present_mask); + halt(); } +#endif /* __SMP__ */ #ifdef CONFIG_RTC /* Reset rtc to defaults. */ { unsigned char control; - cli(); - /* Reset periodic interrupt frequency. */ CMOS_WRITE(0x26, RTC_FREQ_SELECT); @@ -177,22 +163,73 @@ control |= RTC_PIE; CMOS_WRITE(control, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); - - sti(); } -#endif +#endif /* CONFIG_RTC */ - if (!alpha_using_srm && mode != LINUX_REBOOT_CMD_RESTART) { + if (how->mode == LINUX_REBOOT_CMD_RESTART) { + if (!how->restart_cmd) { + *flags |= 0x00020000UL; /* "cold bootstrap" */ + cpup->ipc_buffer[0] = 0; + } else { + /* NOTE: this could really only work when returning + into MILO, rather than SRM console. The latter + does NOT look at the ipc_buffer to get a new + boot command. It could be done by using callbacks + to change some of the SRM environment variables, + but that is beyond our capabilities at this time. + At the moment, SRM will use the last boot device, + but the file and flags will be the defaults, when + doing a "warm" bootstrap. + */ + *flags |= 0x00030000UL; /* "warm bootstrap" */ + strncpy((char *)cpup->ipc_buffer, + how->restart_cmd, + sizeof(cpup->ipc_buffer)); + } + } else + *flags |= 0x00040000UL; /* "remain halted" */ + +#ifdef __SMP__ + /* Wait for the secondaries to halt. */ + clear_bit(smp_boot_cpuid, &cpu_present_mask); + while (cpu_present_mask) + /* Make sure we sample memory and not a register. */ + barrier(); +#endif /* __SMP__ */ + + /* If booted from SRM, reset some of the original environment. */ + if (alpha_using_srm) { +#ifdef CONFIG_DUMMY_CONSOLE + /* This has the effect of resetting the VGA video origin. */ + take_over_console(&dummy_con, 0, MAX_NR_CONSOLES-1, 1); +#endif + reset_for_srm(); + set_hae(srm_hae); + } + else if (how->mode != LINUX_REBOOT_CMD_RESTART && + how->mode != LINUX_REBOOT_CMD_RESTART2) { /* Unfortunately, since MILO doesn't currently understand the hwrpb bits above, we can't reliably halt the processor and keep it halted. So just loop. */ return; } - if (alpha_using_srm) - srm_paging_stop(); - + /* PRIMARY */ halt(); +} + +void +generic_kill_arch(int mode, char * restart_cmd) +{ + struct halt_info copy_of_args; + + copy_of_args.mode = mode; + copy_of_args.restart_cmd = restart_cmd; +#ifdef __SMP__ + /* A secondary can't wait here for the primary to finish, can it now? */ + smp_call_function(halt_processor, (void *)©_of_args, 1, 0); +#endif /* __SMP__ */ + halt_processor(©_of_args); } void diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/kernel/proto.h linux.14p2/arch/alpha/kernel/proto.h --- linux.vanilla/arch/alpha/kernel/proto.h Sat Aug 14 02:27:12 1999 +++ linux.14p2/arch/alpha/kernel/proto.h Fri Oct 22 21:20:22 1999 @@ -153,6 +153,7 @@ extern void handle_ipi(struct pt_regs *); extern void smp_percpu_timer_interrupt(struct pt_regs *); extern int smp_boot_cpuid; +extern unsigned long cpu_present_mask; /* bios32.c */ extern void reset_for_srm(void); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/kernel/ptrace.c linux.14p2/arch/alpha/kernel/ptrace.c --- linux.vanilla/arch/alpha/kernel/ptrace.c Wed Dec 30 00:17:00 1998 +++ linux.14p2/arch/alpha/kernel/ptrace.c Sat Oct 23 23:02:34 1999 @@ -149,13 +149,18 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page; + int fault; DBG(DBG_MEM_ALL, ("getting long at 0x%lx\n", addr)); repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -164,8 +169,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -174,8 +183,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -202,12 +215,17 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -216,8 +234,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -226,13 +248,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } /* This is a hack for non-kernel-mapped video buffers and similar. */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/kernel/signal.c linux.14p2/arch/alpha/kernel/signal.c --- linux.vanilla/arch/alpha/kernel/signal.c Sat Aug 14 02:27:12 1999 +++ linux.14p2/arch/alpha/kernel/signal.c Sat Oct 23 23:02:34 1999 @@ -437,6 +437,8 @@ err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -499,6 +501,8 @@ err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, sw, set->sig[0], oldsp); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/math-emu/fp-emul.c linux.14p2/arch/alpha/math-emu/fp-emul.c --- linux.vanilla/arch/alpha/math-emu/fp-emul.c Sat Aug 28 20:00:53 1999 +++ linux.14p2/arch/alpha/math-emu/fp-emul.c Fri Oct 22 22:30:50 1999 @@ -85,15 +85,15 @@ /* For 128-bit division. */ -__complex__ unsigned long +void udiv128(unsigned long divisor_f0, unsigned long divisor_f1, - unsigned long dividend_f0, unsigned long dividend_f1) + unsigned long dividend_f0, unsigned long dividend_f1, + unsigned long *quot, unsigned long *remd) { _FP_FRAC_DECL_2(quo); _FP_FRAC_DECL_2(rem); _FP_FRAC_DECL_2(tmp); unsigned long i, num_bits, bit; - __complex__ unsigned long ret; _FP_FRAC_SET_2(rem, _FP_ZEROFRAC_2); _FP_FRAC_SET_2(quo, _FP_ZEROFRAC_2); @@ -139,9 +139,9 @@ } out: - __real__ ret = quo_f1; - __imag__ ret = rem_f1; - return ret; + *quot = quo_f1; + *remd = rem_f1; + return; } /* diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/math-emu/sfp-machine.h linux.14p2/arch/alpha/math-emu/sfp-machine.h --- linux.vanilla/arch/alpha/math-emu/sfp-machine.h Sat Aug 28 20:00:53 1999 +++ linux.14p2/arch/alpha/math-emu/sfp-machine.h Fri Oct 22 22:30:50 1999 @@ -552,15 +552,17 @@ : "r" ((UDItype)(u)), \ "r" ((UDItype)(v))) -extern __complex__ unsigned long udiv128(unsigned long, unsigned long, - unsigned long, unsigned long); +extern void udiv128(unsigned long, unsigned long, + unsigned long, unsigned long, + unsigned long *, + unsigned long *); -#define udiv_qrnnd(q, r, n1, n0, d) \ - do { \ - __complex__ unsigned long x_; \ - x_ = udiv128((n0), (n1), 0, (d)); \ - (q) = __real__ x_; \ - (r) = __imag__ x_; \ +#define udiv_qrnnd(q, r, n1, n0, d) \ + do { \ + unsigned long xr, xi; \ + udiv128((n0), (n1), 0, (d), &xr, &xi); \ + (q) = xr; \ + (r) = xi; \ } while (0) #define UDIV_NEEDS_NORMALIZATION 1 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/alpha/mm/fault.c linux.14p2/arch/alpha/mm/fault.c --- linux.vanilla/arch/alpha/mm/fault.c Sat Aug 14 02:27:13 1999 +++ linux.14p2/arch/alpha/mm/fault.c Sat Oct 23 23:02:34 1999 @@ -120,9 +120,18 @@ if (!(vma->vm_flags & VM_WRITE)) goto bad_area; } - handle_mm_fault(current, vma, address, cause > 0); +survive: + { + int fault = handle_mm_fault(current, vma, address, cause > 0); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } up(&mm->mmap_sem); - goto out; + out_unlock: + unlock_kernel(); + return; /* * Something tried to access memory that isn't in our memory map.. @@ -133,9 +142,10 @@ if (user_mode(regs)) { force_sig(SIGSEGV, current); - goto out; + goto out_unlock; } +no_context: /* Are we prepared to handle this fault as an exception? */ if ((fixup = search_exception_table(regs->pc)) != 0) { unsigned long newpc; @@ -143,7 +153,7 @@ printk("%s: Exception at [<%lx>] (%lx)\n", current->comm, regs->pc, newpc); regs->pc = newpc; - goto out; + goto out_unlock; } /* @@ -154,7 +164,37 @@ "virtual address %016lx\n", address); die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16); do_exit(SIGKILL); - out: - unlock_kernel(); -} +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + if (current->pid == 1) + { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + if (user_mode(regs)) + { + printk("VM: killing process %s\n", current->comm); + do_exit(SIGKILL); + } + goto no_context; + +do_sigbus: + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + force_sig(SIGBUS, current); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + goto out_unlock; +} diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/boot/compressed/Makefile linux.14p2/arch/i386/boot/compressed/Makefile --- linux.vanilla/arch/i386/boot/compressed/Makefile Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/i386/boot/compressed/Makefile Fri Oct 22 22:27:46 1999 @@ -37,7 +37,7 @@ tmppiggy=_tmp_$$$$piggy; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk; \ $(OBJCOPY) $(SYSTEM) $$tmppiggy; \ - gzip -f -3 < $$tmppiggy > $$tmppiggy.gz; \ + gzip -f -9 < $$tmppiggy > $$tmppiggy.gz; \ echo "SECTIONS { .data : { input_len = .; LONG(input_data_end - input_data) input_data = .; *(.data) input_data_end = .; }}" > $$tmppiggy.lnk; \ $(LD) -m elf_i386 -r -o piggy.o -b binary $$tmppiggy.gz -b elf32-i386 -T $$tmppiggy.lnk; \ rm -f $$tmppiggy $$tmppiggy.gz $$tmppiggy.lnk diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/boot/compressed/misc.c linux.14p2/arch/i386/boot/compressed/misc.c --- linux.vanilla/arch/i386/boot/compressed/misc.c Wed Jun 24 22:30:08 1998 +++ linux.14p2/arch/i386/boot/compressed/misc.c Fri Oct 22 22:27:14 1999 @@ -104,7 +104,7 @@ #define LOW_BUFFER_START 0x2000 #define LOW_BUFFER_END 0x90000 #define LOW_BUFFER_SIZE ( LOW_BUFFER_END - LOW_BUFFER_START ) -#define HEAP_SIZE 0x2000 +#define HEAP_SIZE 0x2400 static int high_loaded =0; static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/bios32.c linux.14p2/arch/i386/kernel/bios32.c --- linux.vanilla/arch/i386/kernel/bios32.c Sat Aug 14 02:25:55 1999 +++ linux.14p2/arch/i386/kernel/bios32.c Wed Oct 27 18:53:06 1999 @@ -1095,7 +1095,7 @@ * for buggy PCI BIOS'es :-[). */ -extern int skip_ioapic_setup; +extern int skip_ioapic_setup; /* defined in arch/i386/kernel/smp.c */ static void __init pcibios_fixup_devices(void) { diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/io_apic.c linux.14p2/arch/i386/kernel/io_apic.c --- linux.vanilla/arch/i386/kernel/io_apic.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/i386/kernel/io_apic.c Fri Oct 22 21:21:27 1999 @@ -19,7 +19,7 @@ * volatile is justified in this case, IO-APIC register contents * might change spontaneously, GCC should not cache it */ -#define IO_APIC_BASE ((volatile int *)fix_to_virt(FIX_IO_APIC_BASE)) +#define IO_APIC_BASE(idx) ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) /* * The structure of the IO-APIC: @@ -47,7 +47,7 @@ /* * # of IRQ routing registers */ -int nr_ioapic_registers = 0; +int nr_ioapic_registers[MAX_IO_APICS]; enum ioapic_irq_destination_types { dest_Fixed = 0, @@ -94,6 +94,8 @@ mp_ExtINT = 3 }; +int mp_apic_entries = 0; /* # of I/O APIC entries */ +struct mpc_config_ioapic mp_apics[MAX_IO_APICS];/* I/O APIC entries */ int mp_irq_entries = 0; /* # of MP IRQ source entries */ struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; /* MP IRQ source entries */ @@ -108,34 +110,34 @@ * between pins and IRQs. */ -static inline unsigned int io_apic_read(unsigned int reg) +static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg) { - *IO_APIC_BASE = reg; - return *(IO_APIC_BASE+4); + *IO_APIC_BASE(apic) = reg; + return *(IO_APIC_BASE(apic)+4); } -static inline void io_apic_write(unsigned int reg, unsigned int value) +static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) { - *IO_APIC_BASE = reg; - *(IO_APIC_BASE+4) = value; + *IO_APIC_BASE(apic) = reg; + *(IO_APIC_BASE(apic)+4) = value; } /* * Re-write a value: to be used for read-modify-write * cycles where the read already set up the index register. */ -static inline void io_apic_modify(unsigned int value) +static inline void io_apic_modify(unsigned int apic, unsigned int value) { - *(IO_APIC_BASE+4) = value; + *(IO_APIC_BASE(apic)+4) = value; } /* * Synchronize the IO-APIC and the CPU by doing * a dummy read from the IO-APIC */ -static inline void io_apic_sync(void) +static inline void io_apic_sync(unsigned int apic) { - (void) *(IO_APIC_BASE+4); + (void) *(IO_APIC_BASE(apic)+4); } /* @@ -146,7 +148,7 @@ #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) static struct irq_pin_list { - int pin, next; + int apic, pin, next; } irq_2_pin[PIN_MAP_SIZE]; /* @@ -154,7 +156,7 @@ * shared ISA-space IRQs, so we have to support them. We are super * fast in the common case, and fast for shared ISA-space IRQs. */ -static void add_pin_to_irq(unsigned int irq, int pin) +static void add_pin_to_irq(unsigned int irq, int apic, int pin) { static int first_free_entry = NR_IRQS; struct irq_pin_list *entry = irq_2_pin + irq; @@ -168,6 +170,7 @@ if (++first_free_entry >= PIN_MAP_SIZE) panic("io_apic.c: whoops"); } + entry->apic = apic; entry->pin = pin; } @@ -183,9 +186,9 @@ pin = entry->pin; \ if (pin == -1) \ break; \ - reg = io_apic_read(0x10 + R + pin*2); \ + reg = io_apic_read(entry->apic, 0x10 + R + pin*2); \ reg ACTION; \ - io_apic_modify(reg); \ + io_apic_modify(entry->apic, reg); \ if (!entry->next) \ break; \ entry = irq_2_pin + entry->next; \ @@ -197,12 +200,12 @@ * We disable IO-APIC IRQs by setting their 'destination CPU mask' to * zero. Trick by Ramesh Nalluri. */ -DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync()) /* destination = 0x00 */ +DO_ACTION( disable, 1, &= 0x00ffffff, io_apic_sync(entry->apic))/* destination = 0x00 */ DO_ACTION( enable, 1, |= 0xff000000, ) /* destination = 0xff */ -DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync()) /* mask = 1 */ +DO_ACTION( mask, 0, |= 0x00010000, io_apic_sync(entry->apic))/* mask = 1 */ DO_ACTION( unmask, 0, &= 0xfffeffff, ) /* mask = 0 */ -static void clear_IO_APIC_pin(unsigned int pin) +static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) { struct IO_APIC_route_entry entry; @@ -211,16 +214,17 @@ */ memset(&entry, 0, sizeof(entry)); entry.mask = 1; - io_apic_write(0x10 + 2 * pin, *(((int *)&entry) + 0)); - io_apic_write(0x11 + 2 * pin, *(((int *)&entry) + 1)); + io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry) + 0)); + io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry) + 1)); } static void clear_IO_APIC (void) { - int pin; + int apic, pin; - for (pin = 0; pin < nr_ioapic_registers; pin++) - clear_IO_APIC_pin(pin); + for (apic = 0; apic < mp_apic_entries; apic++) + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) + clear_IO_APIC_pin(apic, pin); } /* @@ -270,12 +274,13 @@ /* * Find the IRQ entry number of a certain pin. */ -static int __init find_irq_entry(int pin, int type) +static int __init find_irq_entry(int apic, int pin, int type) { int i; for (i = 0; i < mp_irq_entries; i++) if ( (mp_irqs[i].mpc_irqtype == type) && + (mp_irqs[i].mpc_dstapic == mp_apics[apic].mpc_apicid) && (mp_irqs[i].mpc_dstirq == pin)) return i; @@ -307,21 +312,26 @@ * Find a specific PCI IRQ entry. * Not an initfunc, possibly needed by modules */ +static int __init pin_2_irq(int idx, int apic, int pin); int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) { - int i; + int apic, i; for (i = 0; i < mp_irq_entries; i++) { int lbus = mp_irqs[i].mpc_srcbus; - if (IO_APIC_IRQ(mp_irqs[i].mpc_dstirq) && + for (apic = 0; apic < mp_apic_entries; apic++) + if (mp_apics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) + break; + + if ((apic || IO_APIC_IRQ(mp_irqs[i].mpc_dstirq)) && (mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f)) && (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3))) - return mp_irqs[i].mpc_dstirq; + return pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); } return -1; } @@ -491,9 +501,9 @@ return MPBIOS_trigger(idx); } -static int __init pin_2_irq(int idx, int pin) +static int __init pin_2_irq(int idx, int apic, int pin) { - int irq; + int irq, i; int bus = mp_irqs[idx].mpc_srcbus; /* @@ -513,9 +523,12 @@ case MP_BUS_PCI: /* PCI pin */ { /* - * PCI IRQs are 'directly mapped' + * PCI IRQs are mapped in order */ - irq = pin; + i = irq = 0; + while (i < apic) + irq += nr_ioapic_registers[i++]; + irq += pin; break; } default: @@ -545,12 +558,14 @@ static inline int IO_APIC_irq_trigger(int irq) { - int idx, pin; + int apic, idx, pin; - for (pin = 0; pin < nr_ioapic_registers; pin++) { - idx = find_irq_entry(pin,mp_INT); - if ((idx != -1) && (irq == pin_2_irq(idx,pin))) - return irq_trigger(idx); + for (apic = 0; apic < mp_apic_entries; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { + idx = find_irq_entry(apic,pin,mp_INT); + if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin))) + return irq_trigger(idx); + } } /* * nonexistent IRQs are edge default @@ -582,11 +597,12 @@ void __init setup_IO_APIC_irqs(void) { struct IO_APIC_route_entry entry; - int pin, idx, bus, irq, first_notcon = 1; + int apic, pin, idx, irq, first_notcon = 1; printk("init IO_APIC IRQs\n"); - for (pin = 0; pin < nr_ioapic_registers; pin++) { + for (apic = 0; apic < mp_apic_entries; apic++) { + for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { /* * add it to the IO-APIC irq-routing table: @@ -598,13 +614,13 @@ entry.mask = 0; /* enable IRQ */ entry.dest.logical.logical_dest = 0; /* but no route */ - idx = find_irq_entry(pin,mp_INT); + idx = find_irq_entry(apic,pin,mp_INT); if (idx == -1) { if (first_notcon) { - printk(" IO-APIC pin %d", pin); + printk(" IO-APIC (apicid-pin) %d-%d", mp_apics[apic].mpc_apicid, pin); first_notcon = 0; } else - printk(", %d", pin); + printk(", %d-%d", mp_apics[apic].mpc_apicid, pin); continue; } @@ -617,18 +633,17 @@ entry.dest.logical.logical_dest = 0xff; } - irq = pin_2_irq(idx,pin); - add_pin_to_irq(irq, pin); + irq = pin_2_irq(idx,apic,pin); + add_pin_to_irq(irq, apic, pin); - if (!IO_APIC_IRQ(irq)) + if (!apic && !IO_APIC_IRQ(irq)) continue; entry.vector = assign_irq_vector(irq); - bus = mp_irqs[idx].mpc_srcbus; - - io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); - io_apic_write(0x10+2*pin, *(((int *)&entry)+0)); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + } } if (!first_notcon) @@ -638,7 +653,7 @@ /* * Set up a certain pin as ExtINT delivered interrupt */ -void __init setup_ExtINT_pin(unsigned int pin, int irq) +void __init setup_ExtINT_pin(unsigned int apic, unsigned int pin, int irq) { struct IO_APIC_route_entry entry; @@ -662,8 +677,8 @@ entry.polarity = 0; entry.trigger = 0; - io_apic_write(0x10+2*pin, *(((int *)&entry)+0)); - io_apic_write(0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); + io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); } void __init UNEXPECTED_IO_APIC(void) @@ -674,17 +689,14 @@ void __init print_IO_APIC(void) { - int i; + int apic, i; struct IO_APIC_reg_00 reg_00; struct IO_APIC_reg_01 reg_01; struct IO_APIC_reg_02 reg_02; printk("number of MP IRQ sources: %d.\n", mp_irq_entries); - printk("number of IO-APIC registers: %d.\n", nr_ioapic_registers); - - *(int *)®_00 = io_apic_read(0); - *(int *)®_01 = io_apic_read(1); - *(int *)®_02 = io_apic_read(2); + for (i = 0; i < mp_apic_entries; i++) + printk("number of IO-APIC #%d registers: %d.\n", mp_apics[i].mpc_apicid, nr_ioapic_registers[i]); /* * We are a bit conservative about what we expect. We have to @@ -692,6 +704,12 @@ */ printk("testing the IO APIC.......................\n"); + for (apic = 0; apic < mp_apic_entries; apic++) { + + *(int *)®_00 = io_apic_read(apic, 0); + *(int *)®_01 = io_apic_read(apic, 1); + *(int *)®_02 = io_apic_read(apic, 2); + printk("\nIO APIC #%d......\n", mp_apics[apic].mpc_apicid); printk(".... register #00: %08X\n", *(int *)®_00); printk("....... : physical APIC id: %02X\n", reg_00.ID); if (reg_00.__reserved_1 || reg_00.__reserved_2) @@ -731,8 +749,8 @@ for (i = 0; i <= reg_01.entries; i++) { struct IO_APIC_route_entry entry; - *(((int *)&entry)+0) = io_apic_read(0x10+i*2); - *(((int *)&entry)+1) = io_apic_read(0x11+i*2); + *(((int *)&entry)+0) = io_apic_read(apic, 0x10+i*2); + *(((int *)&entry)+1) = io_apic_read(apic, 0x11+i*2); printk(" %02x %03X %02X ", i, @@ -751,7 +769,7 @@ entry.vector ); } - + } printk(KERN_DEBUG "IRQ to pin mappings:\n"); for (i = 0; i < NR_IRQS; i++) { struct irq_pin_list *entry = irq_2_pin + i; @@ -796,9 +814,12 @@ */ { struct IO_APIC_reg_01 reg_01; + int i; - *(int *)®_01 = io_apic_read(1); - nr_ioapic_registers = reg_01.entries+1; + for (i = 0; i < mp_apic_entries; i++) { + *(int *)®_01 = io_apic_read(i, 1); + nr_ioapic_registers[i] = reg_01.entries+1; + } } /* @@ -897,15 +918,15 @@ /* * Set the ID */ - *(int *)®_00 = io_apic_read(0); + *(int *)®_00 = io_apic_read(0, 0); printk("...changing IO-APIC physical APIC ID to 2...\n"); reg_00.ID = 0x2; - io_apic_write(0, *(int *)®_00); + io_apic_write(0, 0, *(int *)®_00); /* * Sanity check */ - *(int *)®_00 = io_apic_read(0); + *(int *)®_00 = io_apic_read(0, 0); if (reg_00.ID != 0x2) panic("could not set ID"); } @@ -1227,7 +1248,10 @@ if (pin2 != -1) { printk(".. (found pin %d) ...", pin2); - setup_ExtINT_pin(pin2, 0); + /* + * legacy devices should be connected to IO APIC #0 + */ + setup_ExtINT_pin(0, pin2, 0); make_8259A_irq(0); } @@ -1238,9 +1262,9 @@ * Just in case ... */ if (pin1 != -1) - clear_IO_APIC_pin(pin1); + clear_IO_APIC_pin(0, pin1); if (pin2 != -1) - clear_IO_APIC_pin(pin2); + clear_IO_APIC_pin(0, pin2); make_8259A_irq(0); @@ -1282,7 +1306,8 @@ * - those for which the user has specified a pirq= parameter */ if ( ioapic_whitelisted() || - (nr_ioapic_registers == 16) || + (mp_apic_entries == 1 && nr_ioapic_registers[0] == 16) || + (mp_apic_entries > 1) || pirqs_enabled) { printk("ENABLING IO-APIC IRQs\n"); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/irq.h linux.14p2/arch/i386/kernel/irq.h --- linux.vanilla/arch/i386/kernel/irq.h Sat Aug 14 02:27:13 1999 +++ linux.14p2/arch/i386/kernel/irq.h Fri Oct 22 23:30:31 1999 @@ -112,6 +112,11 @@ extern char _stext, _etext; +/* + * IF YOU CHANGE THIS, PLEASE ALSO CHANGE + * FIX_IO_APIC_BASE_* in fixmap.h + */ +#define MAX_IO_APICS 4 #define MAX_IRQ_SOURCES 128 #define MAX_MP_BUSSES 32 enum mp_bustype { diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/ptrace.c linux.14p2/arch/i386/kernel/ptrace.c --- linux.vanilla/arch/i386/kernel/ptrace.c Wed Mar 24 21:18:46 1999 +++ linux.14p2/arch/i386/kernel/ptrace.c Sat Oct 23 23:02:34 1999 @@ -80,12 +80,17 @@ pmd_t * pgmiddle; pte_t * pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (pgd_none(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -94,8 +99,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -104,8 +113,12 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 0); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 0); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return 0; } page = pte_page(*pgtable); /* this is a hack for non-kernel-mapped video buffers and similar */ @@ -131,12 +144,17 @@ pmd_t *pgmiddle; pte_t *pgtable; unsigned long page; + int fault; repeat: pgdir = pgd_offset(vma->vm_mm, addr); if (!pgd_present(*pgdir)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } if (pgd_bad(*pgdir)) { printk("ptrace: bad page directory %08lx\n", pgd_val(*pgdir)); @@ -145,8 +163,12 @@ } pgmiddle = pmd_offset(pgdir, addr); if (pmd_none(*pgmiddle)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } if (pmd_bad(*pgmiddle)) { printk("ptrace: bad page middle %08lx\n", pmd_val(*pgmiddle)); @@ -155,13 +177,21 @@ } pgtable = pte_offset(pgmiddle, addr); if (!pte_present(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } page = pte_page(*pgtable); if (!pte_write(*pgtable)) { - handle_mm_fault(tsk, vma, addr, 1); - goto repeat; + fault = handle_mm_fault(tsk, vma, addr, 1); + if (fault > 0) + goto repeat; + if (fault < 0) + force_sig(SIGKILL, current); + return; } /* this is a hack for non-kernel-mapped video buffers and similar */ if (MAP_NR(page) < max_mapnr) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/setup.c linux.14p2/arch/i386/kernel/setup.c --- linux.vanilla/arch/i386/kernel/setup.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/i386/kernel/setup.c Wed Oct 27 18:51:35 1999 @@ -17,6 +17,9 @@ * * IDT Winchip tweaks, misc clean ups. * Dave Jones , August 1999 + * + * Added proper L2 cache detection for Coppermine + * Dragan Stancevic , October 1999 */ /* @@ -366,7 +369,7 @@ /* Zero is valid according to the BIOS weenies */ if(i386_endbase) { - printk(KERN_NOTICE "Ignoring bogus EBDA pointer %X\n", + printk(KERN_NOTICE "Ignoring bogus EBDA pointer %lX\n", i386_endbase); } i386_endbase = BIOS_ENDBASE; @@ -755,8 +758,8 @@ { X86_VENDOR_INTEL, 6, { "Pentium Pro A-step", "Pentium Pro", NULL, "Pentium II (Klamath)", NULL, "Pentium II (Deschutes)", "Mobile Pentium II", - "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, - NULL, NULL, NULL, NULL }}, + "Pentium III (Katmai)", "Pentium III (Coppermine)", NULL, NULL, + NULL, NULL, NULL, NULL, NULL }}, { X86_VENDOR_AMD, 4, { NULL, NULL, NULL, "486 DX/2", NULL, NULL, NULL, "486 DX/2-WB", "486 DX/4", "486 DX/4-WB", NULL, NULL, NULL, NULL, "Am5x86-WT", @@ -838,6 +841,7 @@ break; case 0x42: + case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ cache_size = 256; break; @@ -1019,8 +1023,9 @@ x86_cap_flags[14] = "mca"; x86_cap_flags[16] = "pat"; x86_cap_flags[17] = "pse36"; - x86_cap_flags[18] = "psn"; - x86_cap_flags[24] = "osfxsr"; + x86_cap_flags[18] = "pn"; + x86_cap_flags[24] = "fxsr"; + x86_cap_flags[25] = "xmm"; break; case X86_VENDOR_CENTAUR: diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/signal.c linux.14p2/arch/i386/kernel/signal.c --- linux.vanilla/arch/i386/kernel/signal.c Sat Aug 14 02:26:46 1999 +++ linux.14p2/arch/i386/kernel/signal.c Sat Oct 23 23:02:34 1999 @@ -419,13 +419,19 @@ ? current->exec_domain->signal_invmap[sig] : sig), &frame->sig); + if (err) + goto give_sigsegv; err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]); + if (err) + goto give_sigsegv; if (_NSIG_WORDS > 1) { err |= __copy_to_user(frame->extramask, &set->sig[1], sizeof(frame->extramask)); } + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -486,6 +492,8 @@ err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= __copy_to_user(&frame->info, info, sizeof(*info)); + if (err) + goto give_sigsegv; /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); @@ -497,6 +505,8 @@ err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; /* Set up to return from userspace. If provided, use a stub already in userspace. */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/kernel/smp.c linux.14p2/arch/i386/kernel/smp.c --- linux.vanilla/arch/i386/kernel/smp.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/i386/kernel/smp.c Fri Oct 22 21:23:03 1999 @@ -128,6 +128,8 @@ const char lk_lockmsg[] = "lock from interrupt context at %p\n"; int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, }; +extern int mp_apic_entries; +extern struct mpc_config_ioapic mp_apics [MAX_IO_APICS]; extern int mp_irq_entries; extern struct mpc_config_intsrc mp_irqs [MAX_IRQ_SOURCES]; extern int mpc_default_type; @@ -367,11 +369,9 @@ printk("I/O APIC #%d Version %d at 0x%lX.\n", m->mpc_apicid,m->mpc_apicver, m->mpc_apicaddr); - /* - * we use the first one only currently - */ - if (ioapics == 1) - mp_ioapic_addr = m->mpc_apicaddr; + mp_apics [mp_apic_entries] = *m; + if (++mp_apic_entries > MAX_IO_APICS) + --mp_apic_entries; } mpt+=sizeof(*m); count+=sizeof(*m); @@ -403,9 +403,9 @@ } } } - if (ioapics > 1) + if (ioapics > MAX_IO_APICS) { - printk("Warning: Multiple IO-APICs not yet supported.\n"); + printk("Warning: Max I/O APICs exceeded (max %d, found %d).\n", MAX_IO_APICS, ioapics); printk("Warning: switching to non APIC mode.\n"); skip_ioapic_setup=1; } @@ -772,18 +772,22 @@ #ifdef CONFIG_X86_IO_APIC { - unsigned long ioapic_phys; + unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; + int i; - if (smp_found_config) { - ioapic_phys = mp_ioapic_addr; - } else { - ioapic_phys = __pa(memory_start); - memset((void *)memory_start, 0, PAGE_SIZE); - memory_start += PAGE_SIZE; + for (i = 0; i < mp_apic_entries; i++) { + if (smp_found_config) { + ioapic_phys = mp_apics[i].mpc_apicaddr; + } else { + ioapic_phys = __pa(memory_start); + memset((void *)memory_start, 0, PAGE_SIZE); + memory_start += PAGE_SIZE; + } + set_fixmap(idx,ioapic_phys); + printk("mapped IOAPIC to %08lx (%08lx)\n", + __fix_to_virt(idx), ioapic_phys); + idx++; } - set_fixmap(FIX_IO_APIC_BASE,ioapic_phys); - printk("mapped IOAPIC to %08lx (%08lx)\n", - fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys); } #endif diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/i386/mm/fault.c linux.14p2/arch/i386/mm/fault.c --- linux.vanilla/arch/i386/mm/fault.c Sun Nov 22 17:38:19 1998 +++ linux.14p2/arch/i386/mm/fault.c Sat Oct 23 23:02:34 1999 @@ -50,7 +50,14 @@ start &= PAGE_MASK; for (;;) { - handle_mm_fault(current,vma, start, 1); + survive: + { + int fault = handle_mm_fault(current,vma, start, 1); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } if (!size) break; size--; @@ -73,6 +80,19 @@ bad_area: return 0; + +do_sigbus: + force_sig(SIGBUS, current); + goto bad_area; + +out_of_memory: + if (current->pid == 1) + { + current->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + goto bad_area; } asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); @@ -162,8 +182,14 @@ * make sure we exit gracefully rather than endlessly redo * the fault. */ - if (!handle_mm_fault(tsk, vma, address, write)) - goto do_sigbus; +survive: + { + int fault = handle_mm_fault(tsk, vma, address, write); + if (!fault) + goto do_sigbus; + if (fault < 0) + goto out_of_memory; + } /* * Did it hit the DOS screen memory VA from vm86 mode? @@ -255,6 +281,34 @@ * We ran out of memory, or some other thing happened to us that made * us unable to handle the page fault gracefully. */ +out_of_memory: + if (tsk->pid == 1) + { + tsk->policy |= SCHED_YIELD; + schedule(); + goto survive; + } + up(&mm->mmap_sem); + if (error_code & 4) + { + if (!((regs->eflags >> 12) & 3)) + { + printk("VM: killing process %s\n", tsk->comm); + do_exit(SIGKILL); + } + else + { + /* + * The task is running with privilegies and so we + * trust it and we give it a chance to die gracefully. + */ + printk("VM: terminating process %s\n", tsk->comm); + force_sig(SIGTERM, current); + return; + } + } + goto no_context; + do_sigbus: up(&mm->mmap_sem); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/common_defconfig linux.14p2/arch/ppc/common_defconfig --- linux.vanilla/arch/ppc/common_defconfig Sat Aug 14 02:27:15 1999 +++ linux.14p2/arch/ppc/common_defconfig Fri Oct 22 22:26:41 1999 @@ -91,8 +91,10 @@ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_DAC960 is not set CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set # @@ -155,7 +157,7 @@ CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -226,10 +228,19 @@ # Network device support # CONFIG_NETDEVICES=y + +# +# ARCnet devices +# # CONFIG_ARCNET is not set # CONFIG_DUMMY is not set # CONFIG_EQUALIZER is not set # CONFIG_ETHERTAP is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# CONFIG_NET_ETHERNET=y CONFIG_MACE=y CONFIG_BMAC=y @@ -238,11 +249,12 @@ # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set # CONFIG_RTL8139 is not set +# CONFIG_SIS900 is not set # CONFIG_YELLOWFIN is not set -# CONFIG_ACENIC is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y CONFIG_PCNET32=y +# CONFIG_ACENIC is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set # CONFIG_CS89x0 is not set @@ -261,7 +273,10 @@ # CONFIG_NET_POCKET is not set # CONFIG_FDDI is not set # CONFIG_HIPPI is not set -# CONFIG_DLCI is not set + +# +# Appletalk devices +# # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set @@ -272,11 +287,22 @@ # # CONFIG_SLIP is not set # CONFIG_NET_RADIO is not set + +# +# Token ring devices +# # CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set # CONFIG_SHAPER is not set + +# +# Wan interfaces +# # CONFIG_HOSTESS_SV11 is not set # CONFIG_COSA is not set -# CONFIG_RCPCI is not set +# CONFIG_SEALEVEL_4021 is not set +# CONFIG_DLCI is not set # # Amateur Radio support @@ -311,7 +337,6 @@ CONFIG_FB_MATROX_MYSTIQUE=y CONFIG_FB_MATROX_G100=y # CONFIG_FB_MATROX_MULTIHEAD is not set -# CONFIG_FB_ATY is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -407,6 +432,7 @@ CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set +# CONFIG_EFS_FS is not set # # Network File Systems @@ -458,6 +484,7 @@ # CONFIG_NLS_ISO8859_7 is not set # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_14 is not set # CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set @@ -468,6 +495,7 @@ CONFIG_DMASOUND=y # CONFIG_SOUND_ES1370 is not set # CONFIG_SOUND_ES1371 is not set +# CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/defconfig linux.14p2/arch/ppc/defconfig --- linux.vanilla/arch/ppc/defconfig Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/ppc/defconfig Fri Oct 22 22:26:41 1999 @@ -9,14 +9,13 @@ CONFIG_6xx=y # CONFIG_PPC64 is not set # CONFIG_8xx is not set -CONFIG_PMAC=y +# CONFIG_PMAC is not set # CONFIG_PREP is not set # CONFIG_CHRP is not set -# CONFIG_ALL_PPC is not set +CONFIG_ALL_PPC=y # CONFIG_APUS is not set # CONFIG_MBX is not set # CONFIG_SMP is not set -CONFIG_MACH_SPECIFIC=y CONFIG_6xx=y # @@ -24,7 +23,7 @@ # CONFIG_EXPERIMENTAL=y CONFIG_MODULES=y -# CONFIG_MODVERSIONS is not set +CONFIG_MODVERSIONS=y CONFIG_KMOD=y CONFIG_PCI=y # CONFIG_PCI_QUIRKS is not set @@ -35,11 +34,10 @@ # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y -CONFIG_BINFMT_MISC=m +# CONFIG_BINFMT_MISC is not set # CONFIG_BINFMT_JAVA is not set -CONFIG_PARPORT=m -# CONFIG_PARPORT_PC is not set -# CONFIG_VGA_CONSOLE is not set +# CONFIG_PARPORT is not set +CONFIG_VGA_CONSOLE=y CONFIG_FB=y CONFIG_FB_COMPAT_XPMAC=y CONFIG_PMAC_PBOOK=y @@ -52,6 +50,7 @@ # CONFIG_TOTALMP is not set CONFIG_BOOTX_TEXT=y # CONFIG_MOTOROLA_HOTSWAP is not set +# CONFIG_CMDLINE_BOOL is not set # # Plug and Play support @@ -61,7 +60,7 @@ # # Block devices # -# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_IDE=y # @@ -75,16 +74,8 @@ # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_BLK_DEV_CMD640 is not set # CONFIG_BLK_DEV_RZ1000 is not set -CONFIG_BLK_DEV_IDEPCI=y -CONFIG_BLK_DEV_IDEDMA=y -# CONFIG_BLK_DEV_OFFBOARD is not set -CONFIG_IDEDMA_AUTO=y -# CONFIG_BLK_DEV_OPTI621 is not set -# CONFIG_BLK_DEV_TRM290 is not set -# CONFIG_BLK_DEV_NS87415 is not set -# CONFIG_BLK_DEV_VIA82C586 is not set -CONFIG_BLK_DEV_CMD646=y -# CONFIG_BLK_DEV_SL82C105 is not set +# CONFIG_BLK_DEV_IDEPCI is not set +CONFIG_BLK_DEV_SL82C105=y CONFIG_BLK_DEV_IDE_PMAC=y CONFIG_BLK_DEV_IDEDMA_PMAC=y CONFIG_BLK_DEV_IDEDMA=y @@ -101,7 +92,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_BLK_DEV_XD is not set # CONFIG_BLK_DEV_DAC960 is not set -CONFIG_PARIDE_PARPORT=m +CONFIG_PARIDE_PARPORT=y # CONFIG_PARIDE is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_DEV_HD is not set @@ -125,7 +116,7 @@ # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set CONFIG_IP_ALIAS=y -# CONFIG_SYN_COOKIES is not set +CONFIG_SYN_COOKIES=y # # (it is safe to leave these untouched) @@ -166,7 +157,7 @@ CONFIG_CHR_DEV_ST=y CONFIG_BLK_DEV_SR=y CONFIG_BLK_DEV_SR_VENDOR=y -# CONFIG_CHR_DEV_SG is not set +CONFIG_CHR_DEV_SG=y # # Some SCSI devices (e.g. CD jukebox) support multiple LUNs @@ -200,14 +191,14 @@ # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_G_NCR5380_PORT is not set +# CONFIG_SCSI_G_NCR5380_MEM is not set # CONFIG_SCSI_INITIO is not set # CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_PPA is not set -# CONFIG_SCSI_IMM is not set # CONFIG_SCSI_NCR53C406A is not set # CONFIG_SCSI_SYM53C416 is not set # CONFIG_SCSI_NCR53C7xx is not set -CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX is not set CONFIG_SCSI_SYM53C8XX=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 @@ -215,7 +206,7 @@ # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set # CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT=y +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PCI2000 is not set # CONFIG_SCSI_PCI2220I is not set @@ -262,7 +253,7 @@ # CONFIG_YELLOWFIN is not set # CONFIG_NET_ISA is not set CONFIG_NET_EISA=y -# CONFIG_PCNET32 is not set +CONFIG_PCNET32=y # CONFIG_ACENIC is not set # CONFIG_AC3200 is not set # CONFIG_APRICOT is not set @@ -289,7 +280,6 @@ # CONFIG_LTPC is not set # CONFIG_COPS is not set # CONFIG_IPDDP is not set -# CONFIG_PLIP is not set CONFIG_PPP=y # @@ -338,12 +328,15 @@ CONFIG_FB_CONTROL=y CONFIG_FB_PLATINUM=y CONFIG_FB_VALKYRIE=y -CONFIG_FB_ATY=y +# CONFIG_FB_ATY is not set CONFIG_FB_IMSTT=y CONFIG_FB_CT65550=y # CONFIG_FB_S3TRIO is not set -# CONFIG_FB_MATROX is not set -CONFIG_FB_ATY=y +CONFIG_FB_MATROX=y +# CONFIG_FB_MATROX_MILLENIUM is not set +CONFIG_FB_MATROX_MYSTIQUE=y +CONFIG_FB_MATROX_G100=y +# CONFIG_FB_MATROX_MULTIHEAD is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FBCON_ADVANCED is not set CONFIG_FBCON_CFB8=y @@ -365,13 +358,22 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -# CONFIG_SERIAL is not set +CONFIG_SERIAL=m # CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 -# CONFIG_PRINTER is not set -# CONFIG_MOUSE is not set +CONFIG_MOUSE=y + +# +# Mice +# +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set # CONFIG_QIC02_TAPE is not set # CONFIG_WATCHDOG is not set CONFIG_NVRAM=y @@ -392,6 +394,14 @@ # Ftape, the floppy tape device driver # # CONFIG_FTAPE is not set +# CONFIG_FT_NORMAL_DEBUG is not set +# CONFIG_FT_FULL_DEBUG is not set +# CONFIG_FT_NO_TRACE is not set +# CONFIG_FT_NO_TRACE_AT_ALL is not set +# CONFIG_FT_STD_FDC is not set +# CONFIG_FT_MACH2 is not set +# CONFIG_FT_PROBE_FC10 is not set +# CONFIG_FT_ALT_FDC is not set # # USB drivers - not for the faint of heart @@ -406,10 +416,10 @@ # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set CONFIG_HFS_FS=y -CONFIG_FAT_FS=m -CONFIG_MSDOS_FS=m +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y # CONFIG_UMSDOS_FS is not set -CONFIG_VFAT_FS=m +CONFIG_VFAT_FS=y CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set @@ -475,7 +485,7 @@ # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_ISO8859_9 is not set # CONFIG_NLS_ISO8859_14 is not set -CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # @@ -489,7 +499,34 @@ # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_MSNDCLAS is not set # CONFIG_SOUND_MSNDPIN is not set -# CONFIG_SOUND_OSS is not set +CONFIG_SOUND_OSS=y +# CONFIG_SOUND_DMAP is not set +# CONFIG_SOUND_PAS is not set +# CONFIG_SOUND_SB is not set +# CONFIG_SOUND_ADLIB is not set +# CONFIG_SOUND_GUS is not set +# CONFIG_SOUND_MPU401 is not set +# CONFIG_SOUND_PSS is not set +# CONFIG_SOUND_MSS is not set +# CONFIG_SOUND_SSCAPE is not set +# CONFIG_SOUND_TRIX is not set +# CONFIG_SOUND_MAD16 is not set +# CONFIG_SOUND_WAVEFRONT is not set +CONFIG_SOUND_CS4232=m +# CONFIG_SOUND_OPL3SA2 is not set +# CONFIG_SOUND_MAUI is not set +# CONFIG_SOUND_SGALAXY is not set +# CONFIG_SOUND_AD1816 is not set +# CONFIG_SOUND_OPL3SA1 is not set +# CONFIG_SOUND_SOFTOSS is not set +# CONFIG_SOUND_YM3812 is not set +# CONFIG_SOUND_VMIDI is not set +# CONFIG_SOUND_UART6850 is not set + +# +# Additional low level sound drivers +# +# CONFIG_LOWLEVEL_SOUND is not set # # Kernel hacking diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/kernel/chrp_pci.c linux.14p2/arch/ppc/kernel/chrp_pci.c --- linux.vanilla/arch/ppc/kernel/chrp_pci.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/ppc/kernel/chrp_pci.c Fri Oct 22 22:26:41 1999 @@ -287,9 +287,6 @@ { if ( dev->irq ) dev->irq = openpic_to_irq( dev->irq ); - /* adjust the io_port for the NCR cards for busses other than 0 -- Cort */ - if ( (dev->bus->number > 0) && (dev->vendor == PCI_VENDOR_ID_NCR) ) - dev->base_address[0] += (dev->bus->number*0x08000000); /* these need to be absolute addrs for OF and Matrox FB -- Cort */ if ( dev->vendor == PCI_VENDOR_ID_MATROX ) { @@ -306,6 +303,10 @@ pcibios_write_config_word(dev->bus->number, dev->devfn, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); } + if ( (dev->bus->number > 0) && + ((dev->vendor == PCI_VENDOR_ID_NCR) || + (dev->vendor == PCI_VENDOR_ID_AMD))) + dev->base_address[0] += (dev->bus->number*0x08000000); } } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/kernel/head.S linux.14p2/arch/ppc/kernel/head.S --- linux.vanilla/arch/ppc/kernel/head.S Sat Aug 28 20:00:53 1999 +++ linux.14p2/arch/ppc/kernel/head.S Fri Oct 22 22:26:41 1999 @@ -1,7 +1,7 @@ /* * arch/ppc/kernel/head.S * - * $Id: head.S,v 1.130.2.3 1999/08/10 21:36:48 cort Exp $ + * $Id: head.S,v 1.130.2.6 1999/10/12 01:03:34 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -112,6 +112,10 @@ /* 601 only have IBAT cr0.eq is set on 601 when using this macro */ #define LOAD_BAT(n, offset, reg, RA, RB) \ + /* see the comment for clear_bats() -- Cort */ \ + li RA,0; \ + mtspr IBAT##n##U,RA; \ + mtspr DBAT##n##U,RA; \ lwz RA,offset+0(reg); \ lwz RB,offset+4(reg); \ mtspr IBAT##n##U,RA; \ @@ -285,6 +289,14 @@ clrldi r16,r16,63 mtsdr1 r16 #else /* CONFIG_PPC64 */ + /* + * If the MMU is off clear the bats. See clear_bat() -- Cort + */ + mfmsr r20 + andi. r20,r20,MSR_DR + bne 100f + bl clear_bats +100: /* * allow secondary cpus to get at all of ram in early bootup * since their init_task may be up there -- Cort @@ -1312,7 +1324,6 @@ #else bnelr- #endif - ori r6,r6,0x100 /* set _PAGE_ACCESSED in pte */ rlwinm r5,r4,5,24,24 /* _PAGE_RW access -> _PAGE_DIRTY */ rlwimi r5,r4,7,22,22 /* _PAGE_RW -> _PAGE_HWWRITE */ @@ -2733,3 +2744,36 @@ .globl cmd_line cmd_line: .space 512 + +/* + * An undocumented "feature" of 604e requires that the v bit + * be cleared before changing BAT values. + * + * Also, newer IBM firmware does not clear bat3 and 4 so + * this makes sure it's done. + * -- Cort + */ +clear_bats: + li r20,0 + + mtspr DBAT0U,r20 + mtspr DBAT0L,r20 + mtspr IBAT0U,r20 + mtspr IBAT0L,r20 + + mtspr DBAT1U,r20 + mtspr DBAT1L,r20 + mtspr IBAT1U,r20 + mtspr IBAT1L,r20 + + mtspr DBAT2U,r20 + mtspr DBAT2L,r20 + mtspr IBAT2U,r20 + mtspr IBAT2L,r20 + + mtspr DBAT3U,r20 + mtspr DBAT3L,r20 + mtspr IBAT3U,r20 + mtspr IBAT3L,r20 + + blr diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/kernel/setup.c linux.14p2/arch/ppc/kernel/setup.c --- linux.vanilla/arch/ppc/kernel/setup.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/ppc/kernel/setup.c Fri Oct 22 22:26:41 1999 @@ -1,5 +1,5 @@ /* - * $Id: setup.c,v 1.132.2.5 1999/09/11 03:32:50 paulus Exp $ + * $Id: setup.c,v 1.132.2.6 1999/10/19 04:32:33 paulus Exp $ * Common prep/pmac/chrp boot and setup code. */ @@ -497,6 +497,28 @@ extern int __map_without_bats; __map_without_bats = 1; } + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } return 0; } @@ -522,7 +544,7 @@ } __initfunc(void setup_arch(char **cmdline_p, - unsigned long * memory_start_p, unsigned long * memory_end_p)) + unsigned long * memory_start_p, unsigned long * memory_end_p)) { extern int panic_timeout; extern char _etext[], _edata[]; @@ -532,8 +554,11 @@ #ifdef CONFIG_XMON extern void xmon_map_scc(void); + char *p; + xmon_map_scc(); - if (strstr(cmd_line, "xmon")) + p = strstr(cmd_line, "xmon"); + if (p != NULL && (p == cmd_line || p[-1] == ' ')) xmon(0); #endif /* CONFIG_XMON */ @@ -547,6 +572,7 @@ /* Save unparsed command line copy for /proc/cmdline */ strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; *memory_start_p = find_available_memory(); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/mm/init.c linux.14p2/arch/ppc/mm/init.c --- linux.vanilla/arch/ppc/mm/init.c Wed Oct 20 01:12:33 1999 +++ linux.14p2/arch/ppc/mm/init.c Fri Oct 22 22:26:41 1999 @@ -1,5 +1,5 @@ /* - * $Id: init.c,v 1.164.2.5 1999/09/07 00:59:22 paulus Exp $ + * $Id: init.c,v 1.164.2.7 1999/10/19 04:32:39 paulus Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) @@ -149,6 +149,9 @@ */ int __map_without_bats = 0; +/* max amount of RAM to use */ +unsigned long __max_memory; + /* optimization for 603 to load the tlb directly from the linux table -- Cort */ #define NO_RELOAD_HTAB 1 /* change in kernel/head.S too! */ @@ -1296,7 +1299,7 @@ int i; /* max amount of RAM we allow -- Cort */ -#define RAM_LIMIT (256<<20) +#define RAM_LIMIT (768<<20) memory_node = find_devices("memory"); if (memory_node == NULL) { @@ -1329,8 +1332,12 @@ * to our nearest IO area. * -- Cort */ - if ( phys_mem.regions[0].size >= RAM_LIMIT ) - phys_mem.regions[0].size = RAM_LIMIT; + if (__max_memory == 0 || __max_memory > RAM_LIMIT) + __max_memory = RAM_LIMIT; + if (phys_mem.regions[0].size >= __max_memory) { + phys_mem.regions[0].size = __max_memory; + phys_mem.n_regions = 1; + } total = phys_mem.regions[0].size; if (phys_mem.n_regions > 1) { @@ -1343,20 +1350,15 @@ if (boot_infos == 0) { /* record which bits the prom is using */ get_mem_prop("available", &phys_avail); + prom_mem = phys_mem; + for (i = 0; i < phys_avail.n_regions; ++i) + remove_mem_piece(&prom_mem, + phys_avail.regions[i].address, + phys_avail.regions[i].size, 0); } else { /* booted from BootX - it's all available (after klimit) */ phys_avail = phys_mem; - } - prom_mem = phys_mem; - for (i = 0; i < phys_avail.n_regions; ++i) - { - if ( phys_avail.regions[i].address >= RAM_LIMIT ) - continue; - if ( (phys_avail.regions[i].address+phys_avail.regions[i].size) - >= RAM_LIMIT ) - phys_avail.regions[i].size = RAM_LIMIT - phys_avail.regions[i].address; - remove_mem_piece(&prom_mem, phys_avail.regions[i].address, - phys_avail.regions[i].size, 1); + prom_mem.n_regions = 0; } /* diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/arch/ppc/xmon/xmon.c linux.14p2/arch/ppc/xmon/xmon.c --- linux.vanilla/arch/ppc/xmon/xmon.c Sat Aug 28 20:00:54 1999 +++ linux.14p2/arch/ppc/xmon/xmon.c Fri Oct 22 22:26:41 1999 @@ -115,9 +115,13 @@ { struct pt_regs regs; int msr, cmd; + static int entered = 0; + + if (!entered) { + entered = 1; + printk("Entering xmon kernel debugger.\n"); + } - printk("Entering xmon kernel debugger.\n"); - if (excp == NULL) { asm volatile ("stw 0,0(%0)\n\ lwz 0,0(1)\n\ @@ -230,7 +234,7 @@ if (dabr.enabled && pc == dabr.instr) return &dabr; - if (iabr.enabled && pc == iabr.address) + if (iabr.enabled && ((pc ^ iabr.address) & ~3) == 0) return &iabr; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) @@ -254,7 +258,9 @@ printf("Couldn't insert breakpoint at %x, disabling\n", bp->address); bp->enabled = 0; + continue; } + store_inst((void *) bp->address); } if (dabr.enabled) set_dabr(dabr.address); @@ -277,9 +283,12 @@ continue; if (mread(bp->address, &instr, 4) == 4 && instr == bpinstr - && mwrite(bp->address, &bp->instr, 4) != 4) + && mwrite(bp->address, &bp->instr, 4) != 4) { printf("Couldn't remove breakpoint at %x\n", bp->address); + continue; + } + store_inst((void *) bp->address); } } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/Config.in linux.14p2/drivers/block/Config.in --- linux.vanilla/drivers/block/Config.in Sat Aug 14 02:27:16 1999 +++ linux.14p2/drivers/block/Config.in Fri Oct 22 22:18:35 1999 @@ -131,7 +131,9 @@ if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then source drivers/block/paride/Config.in fi -tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA +fi if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then define_bool CONFIG_BLK_DEV_HD y diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/genhd.c linux.14p2/drivers/block/genhd.c --- linux.vanilla/drivers/block/genhd.c Wed Oct 20 01:12:34 1999 +++ linux.14p2/drivers/block/genhd.c Fri Oct 22 22:15:20 1999 @@ -784,8 +784,11 @@ label = (struct sun_disklabel *) bh->b_data; p = label->partitions; if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) { +#if 0 + /* There is no error here - it is just not a sunlabel. */ printk("Dev %s Sun disklabel: bad magic %04x\n", kdevname(dev), be16_to_cpu(label->magic)); +#endif brelse(bh); return 0; } @@ -856,8 +859,11 @@ p = &label->partitions[0]; magic = label->magic_mushroom; if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) { +#if 0 + /* There is no error here - it is just not an sgilabel. */ printk("Dev %s SGI disklabel: bad magic %08x\n", kdevname(dev), magic); +#endif brelse(bh); return 0; } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-cd.c linux.14p2/drivers/block/ide-cd.c --- linux.vanilla/drivers/block/ide-cd.c Wed Oct 20 01:12:34 1999 +++ linux.14p2/drivers/block/ide-cd.c Wed Oct 27 18:59:00 1999 @@ -519,7 +519,7 @@ /* Returns 0 if the request should be continued. Returns 1 if the request was ended. */ -static int cdrom_decode_status (ide_drive_t *drive, int good_stat, +static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, int good_stat, int *stat_ret) { struct request *rq = HWGROUP(drive)->rq; @@ -551,7 +551,7 @@ rq->buffer; pc->stat = 1; cdrom_end_request (1, drive); - ide_error (drive, "request sense failure", stat); + *startstop = ide_error (drive, "request sense failure", stat); return 1; } else if (cmd == PACKET_COMMAND) { @@ -633,7 +633,7 @@ } else if ((err & ~ABRT_ERR) != 0) { /* Go to the default handler for other errors. */ - ide_error (drive, "cdrom_decode_status", stat); + *startstop = ide_error (drive, "cdrom_decode_status", stat); return 1; } else if ((++rq->errors > ERROR_MAX)) { /* We've racked up too many retries. Abort. */ @@ -649,6 +649,7 @@ } /* Retry, or handle the next request. */ + *startstop = ide_stopped; return 1; } @@ -660,13 +661,15 @@ called when the interrupt from the drive arrives. Otherwise, HANDLER will be called immediately after the drive is prepared for the transfer. */ -static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen, +static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen, ide_handler_t *handler) { + ide_startstop_t startstop; struct cdrom_info *info = drive->driver_data; /* Wait for the controller to be idle. */ - if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1; + if (ide_wait_stat (&startstop, drive, 0, BUSY_STAT, WAIT_READY)) + return startstop; if (info->dma) info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); @@ -686,12 +689,11 @@ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { ide_set_handler (drive, handler, WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ - (*handler) (drive); + return (*handler) (drive); } - - return 0; } @@ -700,7 +702,7 @@ by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ -static int cdrom_transfer_packet_command (ide_drive_t *drive, +static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, char *cmd_buf, int cmd_len, ide_handler_t *handler) { @@ -708,14 +710,16 @@ /* Here we should have been called after receiving an interrupt from the device. DRQ should how be set. */ int stat_dum; + ide_startstop_t startstop; /* Check for errors. */ - if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) - return 1; + if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum)) + return startstop; } else { + ide_startstop_t startstop; /* Otherwise, we must wait for DRQ to get set. */ - if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) - return 1; + if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) + return startstop; } /* Arm the interrupt handler. */ @@ -724,7 +728,7 @@ /* Send the command to the device. */ atapi_output_bytes (drive, cmd_buf, cmd_len); - return 0; + return ide_started; } @@ -826,12 +830,13 @@ /* * Interrupt routine. Called when a read request has completed. */ -static void cdrom_read_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_read_intr (ide_drive_t *drive) { int stat; int ireason, len, sectors_to_transfer, nskip; struct cdrom_info *info = drive->driver_data; int i, dma = info->dma, dma_error = 0; + ide_startstop_t startstop; struct request *rq = HWGROUP(drive)->rq; @@ -842,8 +847,8 @@ HWIF(drive)->dmaproc(ide_dma_off, drive); } - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; if (dma) { if (!dma_error) { @@ -851,9 +856,9 @@ i -= rq->current_nr_sectors; ide_end_request(1, HWGROUP(drive)); } + return ide_stopped; } else - ide_error (drive, "dma error", stat); - return; + return ide_error (drive, "dma error", stat); } /* Read the interrupt reason and the transfer length. */ @@ -870,11 +875,12 @@ cdrom_end_request (0, drive); } else cdrom_end_request (1, drive); - return; + return ide_stopped; } /* Check that the drive is expecting to do the same thing we are. */ - if (cdrom_read_check_ireason (drive, len, ireason)) return; + if (cdrom_read_check_ireason (drive, len, ireason)) + return ide_stopped; /* Assume that the drive will always provide data in multiples of at least SECTOR_SIZE, as it gets hairy to keep track @@ -889,7 +895,7 @@ CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; } cdrom_end_request (0, drive); - return; + return ide_stopped; } /* The number of sectors we need to read from the drive. */ @@ -952,6 +958,7 @@ /* Done moving data! Wait for another interrupt. */ ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD); + return ide_started; } @@ -1019,7 +1026,7 @@ * However, for drq_interrupt devices, it is called from an interrupt * when the drive is ready to accept the command. */ -static void cdrom_start_read_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive) { struct packet_command pc; struct request *rq = HWGROUP(drive)->rq; @@ -1046,7 +1053,7 @@ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n", drive->name, rq->current_nr_sectors); cdrom_end_request (0, drive); - return; + return ide_stopped; } sector -= nskip; @@ -1072,22 +1079,22 @@ put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]); /* Send the command to the drive and return. */ - (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), - &cdrom_read_intr); + return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_read_intr); } #define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */ #define IDECD_SEEK_TIMER (2 * WAIT_MIN_SLEEP) /* 40 ms */ #define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */ -static void cdrom_seek_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive) { struct cdrom_info *info = drive->driver_data; int stat; static int retry = 10; + ide_startstop_t startstop; - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; CDROM_CONFIG_FLAGS(drive)->seeking = 1; if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) { @@ -1096,9 +1103,10 @@ drive->dsc_overlap = 0; } } + return ide_stopped; } -static void cdrom_start_seek_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive) { struct packet_command pc; struct request *rq = HWGROUP(drive)->rq; @@ -1113,22 +1121,22 @@ memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = SEEK; put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]); - (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); + return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); } -static void cdrom_start_seek (ide_drive_t *drive, unsigned int block) +static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) { struct cdrom_info *info = drive->driver_data; info->dma = 0; info->start_seek = jiffies; - cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); + return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); } /* * Start a read request from the CD-ROM. */ -static void cdrom_start_read (ide_drive_t *drive, unsigned int block) +static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block) { struct cdrom_info *info = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; @@ -1148,7 +1156,7 @@ /* Satisfy whatever we can of this request from our cached sector. */ if (cdrom_read_from_buffer (drive)) - return; + return ide_stopped; /* Clear the local sector buffer. */ info->nsectors_buffered = 0; @@ -1159,8 +1167,7 @@ info->dma = 0; /* Start sending the read request to the drive. */ - cdrom_start_packet_command (drive, 32768, - cdrom_start_read_continuation); + return cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation); } @@ -1178,15 +1185,16 @@ /* Interrupt routine for packet command completion. */ -static void cdrom_pc_intr (ide_drive_t *drive) +static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) { int ireason, len, stat, thislen; struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; + ide_startstop_t startstop; /* Check for errors. */ - if (cdrom_decode_status (drive, 0, &stat)) - return; + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; /* Read the interrupt reason and the transfer length. */ ireason = IN_BYTE (IDE_NSECTOR_REG); @@ -1219,7 +1227,7 @@ pc->stat = 1; cdrom_end_request (1, drive); } - return; + return ide_stopped; } /* Figure out how much data to transfer. */ @@ -1288,21 +1296,21 @@ /* Now we wait for another interrupt. */ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD); + return ide_started; } -static void cdrom_do_pc_continuation (ide_drive_t *drive) +static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; struct packet_command *pc = (struct packet_command *)rq->buffer; /* Send the command to the drive and return. */ - cdrom_transfer_packet_command (drive, pc->c, - sizeof (pc->c), &cdrom_pc_intr); + return cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr); } -static void cdrom_do_packet_command (ide_drive_t *drive) +static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) { int len; struct request *rq = HWGROUP(drive)->rq; @@ -1317,7 +1325,7 @@ pc->stat = 0; /* Start sending the command to the drive. */ - cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); + return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); } @@ -1401,19 +1409,20 @@ /**************************************************************************** * cdrom driver request routine. */ -static -void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t +ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) { if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND) - cdrom_do_packet_command (drive); + return cdrom_do_packet_command (drive); else if (rq -> cmd == RESET_DRIVE_COMMAND) { cdrom_end_request (1, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } else if (rq -> cmd != READ) { printk ("ide-cd: bad cmd %d\n", rq -> cmd); cdrom_end_request (0, drive); + return ide_stopped; } else { + ide_startstop_t action; struct cdrom_info *info = drive->driver_data; if (CDROM_CONFIG_FLAGS(drive)->seeking) { @@ -1423,17 +1432,18 @@ if ((stat & SEEK_STAT) != SEEK_STAT) { if (elpased < IDECD_SEEK_TIMEOUT) { ide_stall_queue (drive, IDECD_SEEK_TIMER); - return; + return ide_stopped; } printk ("%s: DSC timeout\n", drive->name); } CDROM_CONFIG_FLAGS(drive)->seeking = 0; } if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) - cdrom_start_seek (drive, block); + action = cdrom_start_seek (drive, block); else - cdrom_start_read (drive, block); + action = cdrom_start_read (drive, block); info->last_block = block; + return action; } } @@ -2890,8 +2900,11 @@ struct atapi_capabilities_page cap; } buf; - if (CDROM_CONFIG_FLAGS (drive)->nec260) + if (CDROM_CONFIG_FLAGS (drive)->nec260) { + CDROM_CONFIG_FLAGS (drive)->no_eject = 0; + CDROM_CONFIG_FLAGS (drive)->audio_play = 1; return nslots; + } do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ if (attempts-- <= 0) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-disk.c linux.14p2/drivers/block/ide-disk.c --- linux.vanilla/drivers/block/ide-disk.c Thu Mar 11 01:49:43 1999 +++ linux.14p2/drivers/block/ide-disk.c Wed Oct 27 18:56:40 1999 @@ -88,36 +88,41 @@ */ static int lba_capacity_is_ok (struct hd_driveid *id) { - unsigned long lba_sects = id->lba_capacity; - unsigned long chs_sects = id->cyls * id->heads * id->sectors; - unsigned long _10_percent = chs_sects / 10; + unsigned long lba_sects, chs_sects, head, tail; /* - * very large drives (8GB+) may lie about the number of cylinders - * This is a split test for drives 8 Gig and Bigger only. + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. */ - if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) && - (id->heads == 16) && (id->sectors == 63)) { - id->cyls = lba_sects / (16 * 63); /* correct cyls */ - return 1; /* lba_capacity is our only option */ - } + if (id->cyls == 16383 && id->sectors == 63 && + (id->heads == 15 || id->heads == 16) && + id->lba_capacity >= 16383*63*id->heads) + return 1; + + lba_sects = id->lba_capacity; + chs_sects = id->cyls * id->heads * id->sectors; + /* 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 */ - } + if ((lba_sects - chs_sects) < chs_sects/10) + return 1; + /* some drives have the word order reversed */ - lba_sects = (lba_sects << 16) | (lba_sects >> 16); - if ((lba_sects - chs_sects) < _10_percent) { - id->lba_capacity = lba_sects; /* fix it */ + head = ((lba_sects >> 16) & 0xffff); + tail = (lba_sects & 0xffff); + lba_sects = (head | (tail << 16)); + if ((lba_sects - chs_sects) < chs_sects/10) { + id->lba_capacity = lba_sects; return 1; /* lba_capacity is (now) good */ } - return 0; /* lba_capacity value is bad */ + + return 0; /* lba_capacity value may be bad */ } /* * read_intr() is the handler for disk read/multread interrupts */ -static void read_intr (ide_drive_t *drive) +static ide_startstop_t read_intr (ide_drive_t *drive) { byte stat; int i; @@ -125,8 +130,7 @@ struct request *rq; if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { - ide_error(drive, "read_intr", stat); - return; + return ide_error(drive, "read_intr", stat); } msect = drive->mult_count; @@ -138,12 +142,6 @@ msect -= nsect; } else nsect = 1; - /* - * PIO input can take longish times, so we drop the spinlock. - * On SMP, bad things might happen if syscall level code adds - * a new request while we do this PIO, so we just freeze all - * request queue handling while doing the PIO. FIXME - */ idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); #ifdef DEBUG printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n", @@ -160,21 +158,24 @@ if (msect) goto read_next; ide_set_handler (drive, &read_intr, WAIT_CMD); + return ide_started; } + return ide_stopped; } /* * write_intr() is the handler for disk write interrupts */ -static void write_intr (ide_drive_t *drive) +static ide_startstop_t write_intr (ide_drive_t *drive) { byte stat; int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = hwgroup->rq; - int error = 0; - if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat); + } else { #ifdef DEBUG printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n", drive->name, rq->sector, (unsigned long) rq->buffer, @@ -191,25 +192,28 @@ if (i > 0) { idedisk_output_data (drive, rq->buffer, SECTOR_WORDS); ide_set_handler (drive, &write_intr, WAIT_CMD); + return ide_started; } - goto out; + return ide_stopped; } - } else - error = 1; -out: - if (error) - ide_error(drive, "write_intr", stat); + printk("%s: write_intr error2: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat); + } + return ide_error(drive, "write_intr", stat); } /* * ide_multwrite() transfers a block of up to mcount sectors of data * to a drive as part of a disk multiple-sector write operation. + * + * Returns 0 if successful; returns 1 if request had to be aborted due to corrupted buffer list. */ -void ide_multwrite (ide_drive_t *drive, unsigned int mcount) +int ide_multwrite (ide_drive_t *drive, unsigned int mcount) { - struct request *rq = &HWGROUP(drive)->wrq; + ide_hwgroup_t *hwgroup= HWGROUP(drive); + struct request *rq = &hwgroup->wrq; do { + unsigned long flags; unsigned int nsect = rq->current_nr_sectors; if (nsect > mcount) nsect = mcount; @@ -221,6 +225,7 @@ drive->name, rq->sector, (unsigned long) rq->buffer, nsect, rq->nr_sectors - nsect); #endif + spin_lock_irqsave(&io_request_lock, flags); /* Is this really necessary? */ if ((rq->nr_sectors -= nsect) <= 0) break; if ((rq->current_nr_sectors -= nsect) == 0) { @@ -228,32 +233,36 @@ rq->current_nr_sectors = rq->bh->b_size>>9; rq->buffer = rq->bh->b_data; } else { - panic("%s: buffer list corrupted\n", drive->name); - break; + spin_unlock_irqrestore(&io_request_lock, flags); + printk("%s: buffer list corrupted\n", drive->name); + ide_end_request(0, hwgroup); + return 1; } } else { rq->buffer += nsect << 9; } + spin_unlock_irqrestore(&io_request_lock, flags); } while (mcount); + return 0; } /* * multwrite_intr() is the handler for disk multwrite interrupts */ -static void multwrite_intr (ide_drive_t *drive) +static ide_startstop_t multwrite_intr (ide_drive_t *drive) { byte stat; int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup->wrq; - int error = 0; if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { if (stat & DRQ_STAT) { if (rq->nr_sectors) { - ide_multwrite(drive, drive->mult_count); + if (ide_multwrite(drive, drive->mult_count)) + return ide_stopped; ide_set_handler (drive, &multwrite_intr, WAIT_CMD); - goto out; + return ide_started; } } else { if (!rq->nr_sectors) { /* all done? */ @@ -262,20 +271,17 @@ i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } - goto out; + return ide_stopped; } } - } else - error = 1; -out: - if (error) - ide_error(drive, "multwrite_intr", stat); + } + return ide_error(drive, "multwrite_intr", stat); } /* * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. */ -static void set_multmode_intr (ide_drive_t *drive) +static ide_startstop_t set_multmode_intr (ide_drive_t *drive) { byte stat = GET_STAT(); @@ -286,28 +292,31 @@ drive->special.b.recalibrate = 1; (void) ide_dump_status(drive, "set_multmode", stat); } + return ide_stopped; } /* * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. */ -static void set_geometry_intr (ide_drive_t *drive) +static ide_startstop_t set_geometry_intr (ide_drive_t *drive) { byte stat = GET_STAT(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) - ide_error(drive, "set_geometry_intr", stat); + return ide_error(drive, "set_geometry_intr", stat); + return ide_stopped; } /* * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. */ -static void recal_intr (ide_drive_t *drive) +static ide_startstop_t recal_intr (ide_drive_t *drive) { byte stat = GET_STAT(); if (!OK_STAT(stat,READY_STAT,BAD_STAT)) - ide_error(drive, "recal_intr", stat); + return ide_error(drive, "recal_intr", stat); + return ide_stopped; } /* @@ -315,7 +324,7 @@ * using LBA if supported, or CHS otherwise, to address sectors. * It also takes care of issuing special DRIVE_CMDs. */ -static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) { #ifdef CONFIG_BLK_DEV_PDC4030 ide_hwif_t *hwif = HWIF(drive); @@ -361,45 +370,61 @@ } #ifdef CONFIG_BLK_DEV_PDC4030 if (use_pdc4030_io) { - extern void do_pdc4030_io(ide_drive_t *, struct request *); - do_pdc4030_io (drive, rq); - return; + extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *); + return do_pdc4030_io (drive, rq); } #endif /* CONFIG_BLK_DEV_PDC4030 */ if (rq->cmd == READ) { #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) - return; + return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ ide_set_handler(drive, &read_intr, WAIT_CMD); OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); - return; + return ide_started; } if (rq->cmd == WRITE) { + ide_startstop_t startstop; #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) - return; + return ide_started; #endif /* CONFIG_BLK_DEV_IDEDMA */ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, drive->mult_count ? "MULTWRITE" : "WRITE"); - return; + return startstop; } if (!drive->unmask) __cli(); /* local CPU only */ if (drive->mult_count) { - HWGROUP(drive)->wrq = *rq; /* scratchpad */ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + /* + * Ugh.. this part looks ugly because we MUST set up + * the interrupt handler before outputting the first block + * of data to be written. If we hit an error (corrupted buffer list) + * in ide_multwrite(), then we need to remove the handler/timer + * before returning. Fortunately, this NEVER happens (right?). + */ + hwgroup->wrq = *rq; /* scratchpad */ ide_set_handler (drive, &multwrite_intr, WAIT_CMD); - ide_multwrite(drive, drive->mult_count); + if (ide_multwrite(drive, drive->mult_count)) { + unsigned long flags; + spin_lock_irqsave(&io_request_lock, flags); + hwgroup->handler = NULL; + del_timer(&hwgroup->timer); + spin_unlock_irqrestore(&io_request_lock, flags); + return ide_stopped; + } } else { ide_set_handler (drive, &write_intr, WAIT_CMD); idedisk_output_data(drive, rq->buffer, SECTOR_WORDS); } - return; + return ide_started; } printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive) @@ -446,7 +471,6 @@ /* 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; } @@ -454,7 +478,7 @@ return (capacity - drive->sect0); } -static void idedisk_special (ide_drive_t *drive) +static ide_startstop_t idedisk_special (ide_drive_t *drive) { special_t *s = &drive->special; @@ -480,7 +504,9 @@ int special = s->all; s->all = 0; printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special); + return ide_stopped; } + return IS_PDC4030_DRIVE ? ide_stopped : ide_started; } static void idedisk_pre_reset (ide_drive_t *drive) @@ -602,7 +628,7 @@ return -EBUSY; drive->nowerr = arg; drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } @@ -721,9 +747,9 @@ if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) { id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors))); } - drive->cyl = id->cur_cyls = id->cyls; - drive->head = id->cur_heads = id->heads; - drive->sect = id->cur_sectors = id->sectors; + drive->cyl = id->cyls; + drive->head = id->heads; + drive->sect = id->sectors; } /* calculate drive capacity, and select LBA if possible */ @@ -733,21 +759,19 @@ * if possible, give fdisk access to more of the drive, * by correcting bios_cyls: */ - if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && - (!drive->forced_geom) && drive->bios_sect && drive->bios_head) { - drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; -#ifdef DEBUG - printk("Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n", - drive->id->cur_cyls, - drive->id->cur_heads, - drive->id->cur_sectors, - drive->bios_cyl, - drive->bios_head, - drive->bios_sect); -#endif - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (!drive->forced_geom && + capacity > drive->bios_cyl * drive->bios_sect * drive->bios_head) { + unsigned long cylsize; + cylsize = drive->bios_sect * drive->bios_head; + if (cylsize == 0 || capacity/cylsize > 65535) { + drive->bios_sect = 63; + drive->bios_head = 255; + cylsize = 63*255; + } + if (capacity/cylsize > 65535) + drive->bios_cyl = 65535; + else + drive->bios_cyl = capacity/cylsize; } #if 0 /* done instead for entire identify block in arch/ide.h stuff */ @@ -762,7 +786,7 @@ if (drive->using_dma) { if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7)) { - printk(", UDMA"); /* UDMA BIOS-enabled! */ + printk(", UDMA"); /* UDMA BIOS-enabled! */ } else if (id->field_valid & 4) { printk(", (U)DMA"); /* Can be BIOS-enabled! */ } else { @@ -770,19 +794,6 @@ } } printk("\n"); - - if (drive->select.b.lba) { - if (*(int *)&id->cur_capacity0 < id->lba_capacity) { -#ifdef DEBUG - printk(" CurSects=%d, LBASects=%d, ", - *(int *)&id->cur_capacity0, id->lba_capacity); -#endif - *(int *)&id->cur_capacity0 = id->lba_capacity; -#ifdef DEBUG - printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0); -#endif - } - } drive->mult_count = 0; if (id->max_multsect) { diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-dma.c linux.14p2/drivers/block/ide-dma.c --- linux.vanilla/drivers/block/ide-dma.c Sat Aug 14 02:26:27 1999 +++ linux.14p2/drivers/block/ide-dma.c Wed Oct 27 18:56:40 1999 @@ -136,7 +136,7 @@ /* * dma_intr() is the handler for disk read/write DMA interrupts */ -void ide_dma_intr (ide_drive_t *drive) +ide_startstop_t ide_dma_intr (ide_drive_t *drive) { int i; byte stat, dma_stat; @@ -151,12 +151,11 @@ i -= rq->current_nr_sectors; ide_end_request(1, HWGROUP(drive)); } - return; + return ide_stopped; } printk("%s: dma_intr: bad DMA status\n", drive->name); } - ide__sti(); /* local CPU only */ - ide_error(drive, "dma_intr", stat); + return ide_error(drive, "dma_intr", stat); } /* diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-floppy.c linux.14p2/drivers/block/ide-floppy.c --- linux.vanilla/drivers/block/ide-floppy.c Sat Aug 14 02:27:16 1999 +++ linux.14p2/drivers/block/ide-floppy.c Wed Oct 27 18:56:40 1999 @@ -834,7 +834,7 @@ * idefloppy_pc_intr is the usual interrupt handler which will be called * during a packet command. */ -static void idefloppy_pc_intr (ide_drive_t *drive) +static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_status_reg_t status; @@ -879,24 +879,22 @@ rq->errors++; if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { printk (KERN_ERR "ide-floppy: I/O error in request sense command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } idefloppy_retry_pc (drive); /* Retry operation */ - return; + return ide_stopped; /* queued, but not started */ } pc->error = 0; if (floppy->failed_pc == pc) floppy->failed_pc=NULL; pc->callback(drive); /* Command finished - Call the callback function */ - return; + return ide_stopped; } #ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n"); (void) HWIF(drive)->dmaproc(ide_dma_off, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } #endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ @@ -905,14 +903,12 @@ if (ireason.b.cod) { printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read"); printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ temp = pc->actually_transferred + bcount.all; @@ -921,7 +917,7 @@ printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); idefloppy_discard_data (drive,bcount.all); ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); - return; + return ide_started; } #if IDEFLOPPY_DEBUG_LOG printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n"); @@ -943,31 +939,33 @@ pc->current_position+=bcount.all; ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD); /* And set the interrupt handler again */ + return ide_started; } -static void idefloppy_transfer_pc (ide_drive_t *drive) +static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) { + ide_startstop_t startstop; idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_ireason_reg_t ireason; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason.all=IN_BYTE (IDE_IREASON_REG); if (!ireason.b.cod || ireason.b.io) { printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD); /* Set the interrupt routine */ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + return ide_started; } /* * Issue a packet command */ -static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) +static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_bcount_reg_t bcount; @@ -995,7 +993,7 @@ } floppy->failed_pc=NULL; pc->callback(drive); - return; + return ide_stopped; } #if IDEFLOPPY_DEBUG_LOG printk (KERN_INFO "Retry number - %d\n",pc->retries); @@ -1030,9 +1028,10 @@ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); - idefloppy_transfer_pc (drive); + return idefloppy_transfer_pc (drive); } } @@ -1138,7 +1137,7 @@ /* * idefloppy_do_request is our request handling function. */ -static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { idefloppy_floppy_t *floppy = drive->driver_data; idefloppy_pc_t *pc; @@ -1155,7 +1154,7 @@ else printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name); idefloppy_end_request (0, HWGROUP(drive)); - return; + return ide_stopped; } switch (rq->cmd) { case READ: @@ -1163,7 +1162,7 @@ if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) { printk ("%s: unsupported r/w request size\n", drive->name); idefloppy_end_request (0, HWGROUP(drive)); - return; + return ide_stopped; } pc = idefloppy_next_pc_storage (drive); idefloppy_create_rw_cmd (floppy, pc, rq, block); @@ -1174,10 +1173,10 @@ default: printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd); idefloppy_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } pc->rq = rq; - idefloppy_issue_pc (drive, pc); + return idefloppy_issue_pc (drive, pc); } /* diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-probe.c linux.14p2/drivers/block/ide-probe.c --- linux.vanilla/drivers/block/ide-probe.c Mon Mar 22 20:44:18 1999 +++ linux.14p2/drivers/block/ide-probe.c Wed Oct 27 18:56:40 1999 @@ -560,10 +560,6 @@ hwgroup->handler = NULL; hwgroup->drive = NULL; hwgroup->busy = 0; - hwgroup->spinlock = (spinlock_t)SPIN_LOCK_UNLOCKED; -#if (DEBUG_SPINLOCK > 0) - printk("hwgroup(%s) spinlock is %p\n", hwif->name, &hwgroup->spinlock); /* FIXME */ -#endif init_timer(&hwgroup->timer); hwgroup->timer.function = &ide_timer_expiry; hwgroup->timer.data = (unsigned long) hwgroup; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-proc.c linux.14p2/drivers/block/ide-proc.c --- linux.vanilla/drivers/block/ide-proc.c Wed May 6 22:42:53 1998 +++ linux.14p2/drivers/block/ide-proc.c Fri Oct 22 22:15:20 1999 @@ -516,8 +516,10 @@ char *out = page; int len; - out += sprintf(out,"physical %hi/%hi/%hi\n", drive->cyl, drive->head, drive->sect); - out += sprintf(out,"logical %hi/%hi/%hi\n", drive->bios_cyl, drive->bios_head, drive->bios_sect); + out += sprintf(out,"physical %d/%d/%d\n", + drive->cyl, drive->head, drive->sect); + out += sprintf(out,"logical %d/%d/%d\n", + drive->bios_cyl, drive->bios_head, drive->bios_sect); len = out - page; PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide-tape.c linux.14p2/drivers/block/ide-tape.c --- linux.vanilla/drivers/block/ide-tape.c Sat Aug 14 02:27:16 1999 +++ linux.14p2/drivers/block/ide-tape.c Wed Oct 27 18:56:40 1999 @@ -522,7 +522,7 @@ int b_count; byte *buffer; /* Data buffer */ byte *current_position; /* Pointer into the above buffer */ - void (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + ide_startstop_t (*callback) (ide_drive_t *); /* Called when this packet command is completed */ byte pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */ unsigned int flags; /* Status/Action bit flags */ } idetape_pc_t; @@ -1656,7 +1656,7 @@ } } -static void idetape_request_sense_callback (ide_drive_t *drive) +static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -1670,6 +1670,7 @@ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); idetape_end_request (0,HWGROUP (drive)); } + return ide_stopped; } /* @@ -1701,7 +1702,7 @@ * last packet command. We queue a request sense packet command in * the head of the request list. */ -static void idetape_retry_pc (ide_drive_t *drive) +static ide_startstop_t idetape_retry_pc (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc; @@ -1714,6 +1715,7 @@ idetape_create_request_sense_cmd (pc); set_bit (IDETAPE_IGNORE_DSC, &tape->flags); idetape_queue_pc_head (drive, pc, rq); + return ide_stopped; } /* @@ -1724,7 +1726,7 @@ * algorithm described before idetape_issue_packet_command. * */ -static void idetape_pc_intr (ide_drive_t *drive) +static ide_startstop_t idetape_pc_intr (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_status_reg_t status; @@ -1780,11 +1782,9 @@ #endif /* IDETAPE_DEBUG_LOG */ if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { printk (KERN_ERR "ide-tape: I/O error in request sense command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } - idetape_retry_pc (drive); /* Retry operation */ - return; + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; if (test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */ @@ -1792,20 +1792,18 @@ tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST; tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT; idetape_postpone_request (drive); /* Allow ide.c to handle other requests */ - return; + return ide_stopped; } if (tape->failed_pc == pc) tape->failed_pc=NULL; - pc->callback(drive); /* Command finished - Call the callback function */ - return; + return pc->callback(drive); /* Command finished - Call the callback function */ } #ifdef CONFIG_BLK_DEV_IDEDMA if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n"); printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n"); (void) HWIF(drive)->dmaproc(ide_dma_off, drive); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } #endif /* CONFIG_BLK_DEV_IDEDMA */ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ @@ -1814,14 +1812,12 @@ if (ireason.b.cod) { printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read"); printk (KERN_ERR "but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ temp = pc->actually_transferred + bcount.all; @@ -1830,7 +1826,7 @@ printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n"); idetape_discard_data (drive,bcount.all); ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); - return; + return ide_started; } #if IDETAPE_DEBUG_LOG printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n"); @@ -1852,6 +1848,7 @@ pc->current_position+=bcount.all; ide_set_handler (drive,&idetape_pc_intr,IDETAPE_WAIT_CMD); /* And set the interrupt handler again */ + return ide_started; } /* @@ -1897,16 +1894,17 @@ * */ -static void idetape_transfer_pc(ide_drive_t *drive) +static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc = tape->pc; idetape_ireason_reg_t ireason; int retries = 100; + ide_startstop_t startstop; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason.all=IN_BYTE (IDE_IREASON_REG); while (retries-- && (!ireason.b.cod || ireason.b.io)) { @@ -1921,14 +1919,14 @@ } if (!ireason.b.cod || ireason.b.io) { printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD); /* Set the interrupt routine */ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */ + return ide_started; } -static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) +static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) { idetape_tape_t *tape = drive->driver_data; idetape_bcount_reg_t bcount; @@ -1957,8 +1955,7 @@ pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */ } tape->failed_pc=NULL; - pc->callback(drive); - return; + return pc->callback(drive); } #if IDETAPE_DEBUG_LOG printk (KERN_INFO "Retry number - %d\n",pc->retries); @@ -1992,13 +1989,14 @@ if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD); OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; } else { OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); - idetape_transfer_pc(drive); + return idetape_transfer_pc(drive); } } -static void idetape_media_access_finished (ide_drive_t *drive) +static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc = tape->pc; @@ -2008,8 +2006,7 @@ if (status.b.dsc) { if (status.b.check) { /* Error detected */ printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name); - idetape_retry_pc (drive); /* Retry operation */ - return; + return idetape_retry_pc (drive); /* Retry operation */ } pc->error = 0; if (tape->failed_pc == pc) @@ -2018,13 +2015,13 @@ pc->error = IDETAPE_ERROR_GENERAL; tape->failed_pc = NULL; } - pc->callback (drive); + return pc->callback (drive); } /* * General packet command callback function. */ -static void idetape_pc_callback (ide_drive_t *drive) +static ide_startstop_t idetape_pc_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; @@ -2033,9 +2030,10 @@ #endif /* IDETAPE_DEBUG_LOG */ idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive)); + return ide_stopped; } -static void idetape_rw_callback (ide_drive_t *drive) +static ide_startstop_t idetape_rw_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; struct request *rq = HWGROUP(drive)->rq; @@ -2052,6 +2050,7 @@ idetape_end_request (1, HWGROUP (drive)); else idetape_end_request (tape->pc->error, HWGROUP (drive)); + return ide_stopped; } static void idetape_create_locate_cmd (idetape_pc_t *pc, unsigned int block, byte partition) @@ -2170,7 +2169,7 @@ set_bit (PC_DMA_RECOMMENDED, &pc->flags); } -static void idetape_read_position_callback (ide_drive_t *drive) +static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive) { idetape_tape_t *tape = drive->driver_data; idetape_read_position_result_t *result; @@ -2200,6 +2199,7 @@ } } else idetape_end_request (0,HWGROUP (drive)); + return ide_stopped; } static void idetape_create_read_position_cmd (idetape_pc_t *pc) @@ -2213,7 +2213,7 @@ /* * idetape_do_request is our request handling function. */ -static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { idetape_tape_t *tape = drive->driver_data; idetape_pc_t *pc; @@ -2229,24 +2229,23 @@ /* * We do not support buffer cache originated requests. */ - printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue\n", drive->name); + printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue (%d)\n", drive->name, rq->cmd); ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */ - return; + return ide_stopped; } /* * Retry a failed packet command */ if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { - idetape_issue_packet_command (drive, tape->failed_pc); - return; + return idetape_issue_packet_command (drive, tape->failed_pc); } #if IDETAPE_DEBUG_BUGS if (postponed_rq != NULL) if (rq != postponed_rq) { printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n"); idetape_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } #endif /* IDETAPE_DEBUG_BUGS */ @@ -2266,15 +2265,16 @@ tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT; } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) { printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name); - if (rq->cmd == IDETAPE_PC_RQ2) + if (rq->cmd == IDETAPE_PC_RQ2) { idetape_media_access_finished (drive); - else - ide_do_reset (drive); - return; + return ide_stopped; + } else { + return ide_do_reset (drive); + } } else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD) tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW; idetape_postpone_request (drive); - return; + return ide_stopped; } switch (rq->cmd) { case IDETAPE_READ_RQ: @@ -2289,20 +2289,20 @@ rq->cmd = IDETAPE_WRITE_RQ; rq->errors = IDETAPE_ERROR_EOD; idetape_end_request (1, HWGROUP(drive)); - return; + return ide_stopped; case IDETAPE_PC_RQ1: pc=(idetape_pc_t *) rq->buffer; rq->cmd = IDETAPE_PC_RQ2; break; case IDETAPE_PC_RQ2: idetape_media_access_finished (drive); - return; + return ide_stopped; default: printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n"); idetape_end_request (0,HWGROUP (drive)); - return; + return ide_stopped; } - idetape_issue_packet_command (drive, pc); + return idetape_issue_packet_command (drive, pc); } /* diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide.c linux.14p2/drivers/block/ide.c --- linux.vanilla/drivers/block/ide.c Wed Oct 20 01:12:34 1999 +++ linux.14p2/drivers/block/ide.c Wed Oct 27 18:56:41 1999 @@ -433,7 +433,7 @@ #if 0 udelay(1); /* need to guarantee 400ns since last command was issued */ #endif - if (GET_STAT() & BUSY_STAT) + if (GET_STAT() & BUSY_STAT) /* Note: this may clear a pending IRQ!! */ return 0; /* drive busy: definitely not interrupting */ return 1; /* drive ready: *might* be interrupting */ } @@ -471,17 +471,15 @@ unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); - spin_lock_irqsave(&hwgroup->spinlock, flags); -#ifdef DEBUG + spin_lock_irqsave(&io_request_lock, flags); if (hwgroup->handler != NULL) { printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n", drive->name, hwgroup->handler, handler); } -#endif hwgroup->handler = handler; hwgroup->timer.expires = jiffies + timeout; - add_timer(&(hwgroup->timer)); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + add_timer(&hwgroup->timer); + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -516,7 +514,7 @@ } } -static void do_reset1 (ide_drive_t *, int); /* needed below */ +static ide_startstop_t do_reset1 (ide_drive_t *, int); /* needed below */ /* * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms @@ -524,7 +522,7 @@ * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */ -static void atapi_reset_pollfunc (ide_drive_t *drive) +static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); byte stat; @@ -537,14 +535,14 @@ } else { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); - return; /* continue polling */ + return ide_started; /* continue polling */ } hwgroup->poll_timeout = 0; /* end of polling */ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); - do_reset1 (drive, 1); /* do it the old fashioned way */ - return; + return do_reset1 (drive, 1); /* do it the old fashioned way */ } hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; } /* @@ -553,7 +551,7 @@ * and we have not yet hit our maximum waiting time, then the timer is restarted * for another 50ms. */ -static void reset_pollfunc (ide_drive_t *drive) +static ide_startstop_t reset_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); ide_hwif_t *hwif = HWIF(drive); @@ -562,7 +560,7 @@ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) { ide_set_handler (drive, &reset_pollfunc, HZ/20); - return; /* continue polling */ + return ide_started; /* continue polling */ } printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); } else { @@ -594,6 +592,7 @@ } } hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; } static void pre_reset (ide_drive_t *drive) @@ -623,7 +622,7 @@ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, * we set a timer to poll at 50ms intervals. */ -static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi) +static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) { unsigned int unit; unsigned long flags; @@ -642,7 +641,7 @@ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20); __restore_flags (flags); /* local CPU only */ - return; + return ide_started; } /* @@ -670,14 +669,15 @@ #endif /* OK_TO_RESET_CONTROLLER */ __restore_flags (flags); /* local CPU only */ + return ide_started; } /* * ide_do_reset() is the entry point to the drive/interface reset code. */ -void ide_do_reset (ide_drive_t *drive) +ide_startstop_t ide_do_reset (ide_drive_t *drive) { - do_reset1 (drive, 0); + return do_reset1 (drive, 0); } /* @@ -703,11 +703,8 @@ HWGROUP(drive)->rq = NULL; rq->rq_status = RQ_INACTIVE; spin_unlock_irqrestore(&io_request_lock, flags); - save_flags(flags); /* all CPUs; overkill? */ - cli(); /* all CPUs; overkill? */ if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ - restore_flags(flags); /* all CPUs; overkill? */ } /* @@ -800,19 +797,19 @@ /* * ide_error() takes action based on the error returned by the drive. */ -void ide_error (ide_drive_t *drive, const char *msg, byte stat) +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat) { struct request *rq; byte err; err = ide_dump_status(drive, msg, stat); if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) - return; + return ide_stopped; /* retry only "normal" I/O: */ if (rq->cmd == IDE_DRIVE_CMD) { rq->errors = 1; ide_end_drive_cmd(drive, stat, err); - return; + return ide_stopped; } if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ rq->errors |= ERROR_RESET; @@ -821,7 +818,7 @@ /* err has different meaning on cdrom and tape */ 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 */ + return ide_stopped; /* 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 */ @@ -843,12 +840,13 @@ } else { if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; - ide_do_reset(drive); - return; - } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) drive->special.b.recalibrate = 1; ++rq->errors; } + return ide_stopped; } /* @@ -866,7 +864,7 @@ /* * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. */ -static void drive_cmd_intr (ide_drive_t *drive) +static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) { struct request *rq = HWGROUP(drive)->rq; byte *args = (byte *) rq->buffer; @@ -883,17 +881,17 @@ udelay(100); } - if (OK_STAT(stat, READY_STAT, BAD_STAT)) - ide_end_drive_cmd (drive, stat, GET_ERR()); - else - ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + return ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ + ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; } /* * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT * commands to a drive. It used to do much more, but has been scaled back. */ -static inline void do_special (ide_drive_t *drive) +static ide_startstop_t do_special (ide_drive_t *drive) { special_t *s = &drive->special; @@ -906,11 +904,12 @@ if (tuneproc != NULL) tuneproc(drive, drive->tune_req); } else if (drive->driver != NULL) { - DRIVER(drive)->special(drive); + return DRIVER(drive)->special(drive); } else if (s->all) { printk("%s: bad special flag: 0x%02x\n", drive->name, s->all); s->all = 0; } + return ide_stopped; } /* @@ -924,12 +923,12 @@ * setting a timer to wake up at half second intervals thereafter, * until timeout is achieved, before timing out. */ -int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout) +int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) { byte stat; int i; unsigned long flags; - + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ if ((stat = GET_STAT()) & BUSY_STAT) { __save_flags(flags); /* local CPU only */ @@ -938,7 +937,7 @@ while ((stat = GET_STAT()) & BUSY_STAT) { if (0 < (signed long)(jiffies - timeout)) { __restore_flags(flags); /* local CPU only */ - ide_error(drive, "status timeout", stat); + *startstop = ide_error(drive, "status timeout", stat); return 1; } } @@ -956,7 +955,7 @@ if (OK_STAT((stat = GET_STAT()), good, bad)) return 0; } - ide_error(drive, "status error", stat); + *startstop = ide_error(drive, "status error", stat); return 1; } @@ -964,7 +963,7 @@ * execute_drive_cmd() issues a special drive command, * usually initiated by ioctl() from the external hdparm program. */ -static void execute_drive_cmd (ide_drive_t *drive, struct request *rq) +static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) { byte *args = rq->buffer; if (args) { @@ -978,7 +977,7 @@ } OUT_BYTE(args[2],IDE_FEATURE_REG); ide_cmd(drive, args[0], args[1], &drive_cmd_intr); - return; + return ide_started; } else { /* * NULL is actually a valid way of waiting for @@ -988,21 +987,21 @@ printk("%s: DRIVE_CMD (null)\n", drive->name); #endif ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); - return; + return ide_stopped; } } /* * start_request() initiates handling of a new I/O request */ -static inline void start_request (ide_drive_t *drive) +static ide_startstop_t start_request (ide_drive_t *drive) { + ide_startstop_t startstop; unsigned long block, blockend; struct request *rq = drive->queue; unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS; ide_hwif_t *hwif = HWIF(drive); - ide__sti(); /* local CPU only */ #ifdef DEBUG printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq); #endif @@ -1032,29 +1031,27 @@ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME); #endif SELECT_DRIVE(hwif, drive); - if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { + if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { printk("%s: drive not ready for command\n", drive->name); - return; + return startstop; } if (!drive->special.all) { if (rq->cmd == IDE_DRIVE_CMD) { - execute_drive_cmd(drive, rq); - return; + return execute_drive_cmd(drive, rq); } if (drive->driver != NULL) { - DRIVER(drive)->do_request(drive, rq, block); - return; + return (DRIVER(drive)->do_request(drive, rq, block)); } printk("%s: media type %d not supported\n", drive->name, drive->media); goto kill_rq; } - do_special(drive); - return; + return do_special(drive); kill_rq: if (drive->driver != NULL) DRIVER(drive)->end_request(0, HWGROUP(drive)); else ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } /* @@ -1115,22 +1112,54 @@ } /* - * Caller must have already acquired spinlock using *spinflags + * Issue a new request to a drive from hwgroup + * Caller must have already done spin_lock_irqsave(&io_request_lock, ..); + * + * A hwgroup is a serialized group of IDE interfaces. Usually there is + * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640) + * may have both interfaces in a single hwgroup to "serialize" access. + * Or possibly multiple ISA interfaces can share a common IRQ by being grouped + * together into one hwgroup for serialized access. + * + * Note also that several hwgroups can end up sharing a single IRQ, + * possibly along with many other devices. This is especially common in + * PCI-based systems with off-board IDE controller cards. + * + * The IDE driver uses the single global io_request_lock spinlock to protect + * access to the request queues, and to protect the hwgroup->busy flag. + * + * The first thread into the driver for a particular hwgroup sets the + * hwgroup->busy flag to indicate that this hwgroup is now active, + * and then initiates processing of the top request from the request queue. + * + * Other threads attempting entry notice the busy setting, and will simply + * queue their new requests and exit immediately. Note that hwgroup->busy + * remains set even when the driver is merely awaiting the next interrupt. + * Thus, the meaning is "this hwgroup is busy processing a request". + * + * When processing of a request completes, the completing thread or IRQ-handler + * will start the next request from the queue. If no more work remains, + * the driver will clear the hwgroup->busy flag and exit. + * + * The io_request_lock (spinlock) is used to protect all access to the + * hwgroup->busy flag, but is otherwise not needed for most processing in + * the driver. This makes the driver much more friendlier to shared IRQs + * than previous designs, while remaining 100% (?) SMP safe and capable. */ -static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags, int masked_irq) +static void ide_do_request (ide_hwgroup_t *hwgroup) { struct blk_dev_struct *bdev; ide_drive_t *drive; ide_hwif_t *hwif; - unsigned long io_flags; + ide_startstop_t startstop; + + ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only: POSSIBLY BROKEN HERE(?) */ - hwgroup->busy = 1; - while (hwgroup->handler == NULL) { - spin_lock_irqsave(&io_request_lock, io_flags); + while (!hwgroup->busy) { + hwgroup->busy = 1; drive = choose_drive(hwgroup); if (drive == NULL) { unsigned long sleep = 0; - hwgroup->rq = NULL; drive = hwgroup->drive; do { @@ -1140,24 +1169,34 @@ if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep))) sleep = drive->sleep; } while ((drive = drive->next) != hwgroup->drive); - spin_unlock_irqrestore(&io_request_lock, io_flags); if (sleep) { + /* + * Take a short snooze, and then wake up this hwgroup again. + * This gives other hwgroups on the same a chance to + * play fairly with us, just in case there are big differences + * in relative throughputs.. don't want to hog the cpu too much. + * + * Mmmm.. note we also do hwgroup->busy=0 below, which means + * we will also be woken up if somebody enqueues another + * request against this hwgroup while we're snoozing. + * We could "fix" that by setting hwgroup->busy=1 instead, + * but that would make the error handling more complicated + * in ide_timer_expiry() -- this is good enough for now. + */ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) sleep = jiffies + WAIT_MIN_SLEEP; -#if 1 - if (hwgroup->timer.next || hwgroup->timer.prev) - printk("ide_set_handler: timer already active\n"); -#endif + hwgroup->sleeping = 1; /* so that ide_timer_expiry knows what to do */ mod_timer(&hwgroup->timer, sleep); + /* we purposely leave hwgroup->busy==1 while sleeping */ } else { /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */ ide_release_lock(&ide_lock); /* for atari only */ + hwgroup->busy = 0; } - hwgroup->busy = 0; - return; + return; /* no more work for this hwgroup (for now) */ } hwif = HWIF(drive); - if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */ + if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */ OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]); hwgroup->hwif = hwif; hwgroup->drive = drive; @@ -1168,15 +1207,13 @@ if (bdev->current_request == &bdev->plug) /* FIXME: paranoia */ printk("%s: Huh? nuking plugged queue\n", drive->name); bdev->current_request = hwgroup->rq = drive->queue; - spin_unlock_irqrestore(&io_request_lock, io_flags); - - if (hwif->irq != masked_irq) - disable_irq_nosync(hwif->irq); - spin_unlock_irqrestore(&hwgroup->spinlock, *hwgroup_flags); - start_request(drive); - spin_lock_irqsave(&hwgroup->spinlock, *hwgroup_flags); - if (hwif->irq != masked_irq) - enable_irq(hwif->irq); + spin_unlock(&io_request_lock); + if (!hwif->serialized) /* play it safe with buggy hardware */ + ide__sti(); + startstop = start_request(drive); + spin_lock_irq(&io_request_lock); + if (startstop == ide_stopped) + hwgroup->busy = 0; } } @@ -1190,134 +1227,112 @@ return &hwif->drives[DEVICE_NR(dev) & 1].queue; } -/* - * do_hwgroup_request() invokes ide_do_request() after claiming hwgroup->busy. - */ -static void do_hwgroup_request (ide_hwgroup_t *hwgroup) -{ - unsigned long flags; - - spin_lock_irqsave(&hwgroup->spinlock, flags); - if (hwgroup->busy) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - return; - } - del_timer(&hwgroup->timer); - ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only */ - ide_do_request(hwgroup, &flags, 0); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); -} - -/* - * ll_rw_blk.c invokes our do_idex_request() function - * with the io_request_spinlock already grabbed. - * Since we need to do our own spinlock's internally, - * on paths that don't necessarily originate through the - * do_idex_request() path, we have to undo the spinlock on entry, - * and restore it again on exit. - * Fortunately, this is mostly a nop for non-SMP kernels. - */ -static inline void unlock_do_hwgroup_request (ide_hwgroup_t *hwgroup) -{ - spin_unlock(&io_request_lock); - do_hwgroup_request (hwgroup); - spin_lock_irq(&io_request_lock); -} - void do_ide0_request (void) { - unlock_do_hwgroup_request (ide_hwifs[0].hwgroup); + ide_do_request (ide_hwifs[0].hwgroup); } #if MAX_HWIFS > 1 void do_ide1_request (void) { - unlock_do_hwgroup_request (ide_hwifs[1].hwgroup); + ide_do_request (ide_hwifs[1].hwgroup); } #endif /* MAX_HWIFS > 1 */ #if MAX_HWIFS > 2 void do_ide2_request (void) { - unlock_do_hwgroup_request (ide_hwifs[2].hwgroup); + ide_do_request (ide_hwifs[2].hwgroup); } #endif /* MAX_HWIFS > 2 */ #if MAX_HWIFS > 3 void do_ide3_request (void) { - unlock_do_hwgroup_request (ide_hwifs[3].hwgroup); + ide_do_request (ide_hwifs[3].hwgroup); } #endif /* MAX_HWIFS > 3 */ #if MAX_HWIFS > 4 void do_ide4_request (void) { - unlock_do_hwgroup_request (ide_hwifs[4].hwgroup); + ide_do_request (ide_hwifs[4].hwgroup); } #endif /* MAX_HWIFS > 4 */ #if MAX_HWIFS > 5 void do_ide5_request (void) { - unlock_do_hwgroup_request (ide_hwifs[5].hwgroup); + ide_do_request (ide_hwifs[5].hwgroup); } #endif /* MAX_HWIFS > 5 */ -static void start_next_request (ide_hwgroup_t *hwgroup, int masked_irq) -{ - unsigned long flags; - ide_drive_t *drive; - - spin_lock_irqsave(&hwgroup->spinlock, flags); - if (hwgroup->handler != NULL) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - return; - } - drive = hwgroup->drive; - set_recovery_timer(HWIF(drive)); - drive->service_time = jiffies - drive->service_start; - ide_do_request(hwgroup, &flags, masked_irq); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); -} - +/* + * ide_timer_expiry() is our timeout function for all drive operations. + * But note that it can also be invoked as a result of a "sleep" operation + * triggered by the mod_timer() call in ide_do_request. + */ void ide_timer_expiry (unsigned long data) { - ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; - ide_drive_t *drive; - ide_handler_t *handler; - unsigned long flags; + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; + ide_handler_t *handler; + unsigned long flags; - spin_lock_irqsave(&hwgroup->spinlock, flags); - drive = hwgroup->drive; + spin_lock_irqsave(&io_request_lock, flags); + del_timer(&hwgroup->timer); if ((handler = hwgroup->handler) == NULL) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - do_hwgroup_request(hwgroup); - return; - } - hwgroup->busy = 1; /* should already be "1" */ - hwgroup->handler = NULL; - del_timer(&hwgroup->timer); /* Is this needed?? */ - if (hwgroup->poll_timeout != 0) { /* polling in progress? */ - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - handler(drive); - } else if (drive_is_ready(drive)) { - printk("%s: lost interrupt\n", drive->name); - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - handler(drive); + /* + * Either a marginal timeout occured (got the interrupt just as timer expired), + * or we were "sleeping" to give other devices a chance. Either way, we don't + * really want to complain about anything. + */ + if (hwgroup->sleeping) { + hwgroup->sleeping = 0; + hwgroup->busy = 0; + } } else { - if (drive->waiting_for_dma) { - (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); - printk("%s: timeout waiting for DMA\n", drive->name); - /* - * need something here for HX PIIX3 UDMA and HPT343.......AMH - * irq timeout: status=0x58 { DriveReady SeekComplete DataRequest } - */ + ide_drive_t *drive = hwgroup->drive; + hwgroup->handler = NULL; + if (!drive) { + printk("ide_timer_expiry: hwgroup->drive was NULL\n"); + } else { + ide_hwif_t *hwif; + ide_startstop_t startstop; + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name); + } + /* + * We need to simulate a real interrupt when invoking the handler() + * function, which means we need to globally mask the specific IRQ: + */ + spin_unlock(&io_request_lock); + hwif = HWIF(drive); + disable_irq(hwif->irq); + __cli(); /* local CPU only, as if we were handling an interrupt */ + if (hwgroup->poll_timeout != 0 || drive_is_ready(drive)) { + if (hwgroup->poll_timeout == 0) { + printk("%s: lost interrupt\n", drive->name); + (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); + } + startstop = handler(drive); + } else { + if (drive->waiting_for_dma) { + (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); + printk("%s: timeout waiting for DMA\n", drive->name); + } + startstop = ide_error(drive, "irq timeout", GET_STAT()); + } + set_recovery_timer(hwif); + drive->service_time = jiffies - drive->service_start; + enable_irq(hwif->irq); + spin_lock_irq(&io_request_lock); + if (startstop == ide_stopped) + hwgroup->busy = 0; } - spin_unlock_irqrestore(&hwgroup->spinlock, flags); - ide_error(drive, "irq timeout", GET_STAT()); } - start_next_request(hwgroup, 0); + ide_do_request(hwgroup); + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1377,9 +1392,9 @@ ide_hwif_t *hwif; ide_drive_t *drive; ide_handler_t *handler; + ide_startstop_t startstop; - __cli(); /* local CPU only */ - spin_lock_irqsave(&hwgroup->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); hwif = hwgroup->hwif; if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) { /* @@ -1404,41 +1419,63 @@ */ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); unexpected_intr(irq, hwgroup); + } else { + /* + * Whack the status register, just in case we have a leftover pending IRQ. + */ + (void)IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); } - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return; } drive = hwgroup->drive; - if (!drive || !drive_is_ready(drive)) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + if (!drive) { + /* + * This should NEVER happen, and there isn't much we could do about it here. + */ + spin_unlock_irqrestore(&io_request_lock, flags); return; } + if (!drive_is_ready(drive)) { + /* + * This happens regularly when we share a PCI IRQ with another device. + * Unfortunately, it can also happen with some buggy drives that trigger + * the IRQ before their status register is up to date. Hopefully we have + * enough advance overhead that the latter isn't a problem. + */ + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); + } hwgroup->handler = NULL; + del_timer(&hwgroup->timer); (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); - del_timer(&(hwgroup->timer)); - { - struct request *rq; - unsigned long block, sectors; - - if ((rq = hwgroup->rq) != NULL) { - block = rq->sector; - block += drive->part[MINOR(rq->rq_dev)&PARTN_MASK].start_sect + drive->sect0; - sectors = drive->using_dma ? rq->nr_sectors : drive->mult_count ? drive->mult_count : 1; - } - } - - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock(&io_request_lock); if (drive->unmask) ide__sti(); /* local CPU only */ - handler(drive); /* service this interrupt, may set handler for next interrupt */ + startstop = handler(drive); /* service this interrupt, may set handler for next interrupt */ + spin_lock_irq(&io_request_lock); /* * Note that handler() may have set things up for another * interrupt to occur soon, but it cannot happen until * we exit from this routine, because it will be the - * same irq as is currently being serviced here, - * and Linux won't allow another (on any CPU) until we return. + * same irq as is currently being serviced here, and Linux + * won't allow another of the same (on any CPU) until we return. */ - start_next_request(hwgroup, hwif->irq); + set_recovery_timer(HWIF(drive)); + drive->service_time = jiffies - drive->service_start; + if (startstop == ide_stopped) { + if (hwgroup->handler == NULL) { /* paranoia */ + hwgroup->busy = 0; + ide_do_request(hwgroup); + } else { + printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name); + } + } + spin_unlock_irqrestore(&io_request_lock, flags); } /* @@ -1521,7 +1558,6 @@ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<sem = &sem; - spin_lock_irqsave(&io_request_lock, flags); cur_rq = drive->queue; if (cur_rq == NULL || action == ide_preempt) { @@ -1537,16 +1573,13 @@ rq->next = cur_rq->next; cur_rq->next = rq; } + ide_do_request(hwgroup); spin_unlock_irqrestore(&io_request_lock, flags); - do_hwgroup_request(hwgroup); - save_flags(flags); /* all CPUs; overkill? */ - cli(); /* all CPUs; overkill? */ - if (action == ide_wait) - { - down(&sem); /* wait for it to be serviced */ + if (action == ide_wait) { + down(&sem); /* wait for it to be serviced */ + return rq->errors ? -EIO : 0; /* return -EIO if errors */ } - restore_flags(flags); /* all CPUs; overkill? */ - return rq->errors ? -EIO : 0; /* return -EIO if errors */ + return 0; } /* @@ -1569,14 +1602,14 @@ major = MAJOR(i_rdev); minor = drive->select.b.unit << PARTN_BITS; hwgroup = HWGROUP(drive); - spin_lock_irqsave(&hwgroup->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); if (drive->busy || (drive->usage > 1)) { - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return -EBUSY; }; drive->busy = 1; MOD_INC_USE_COUNT; - spin_unlock_irqrestore(&hwgroup->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); for (p = 0; p < (1<part[p].nr_sects > 0) { @@ -1962,7 +1995,7 @@ unsigned long flags; if ((setting->rw & SETTING_READ)) { - spin_lock_irqsave(&HWGROUP(drive)->spinlock, flags); + spin_lock_irqsave(&io_request_lock, flags); switch(setting->data_type) { case TYPE_BYTE: val = *((u8 *) setting->data); @@ -1975,7 +2008,7 @@ val = *((u32 *) setting->data); break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); } return val; } @@ -1985,19 +2018,28 @@ ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned long timeout = jiffies + (3 * HZ); - spin_lock_irqsave(&hwgroup->spinlock, *flags); + spin_lock_irqsave(&io_request_lock, *flags); while (hwgroup->busy) { - spin_unlock_irqrestore(&hwgroup->spinlock, *flags); - __sti(); /* local CPU only; needed for jiffies */ + unsigned long lflags; + spin_unlock_irqrestore(&io_request_lock, *flags); + __save_flags(lflags); /* local CPU only */ + __sti(); /* local CPU only; needed for jiffies */ if (0 < (signed long)(jiffies - timeout)) { + __restore_flags(lflags); /* local CPU only */ printk("%s: channel busy\n", drive->name); return -EBUSY; } - spin_lock_irqsave(&hwgroup->spinlock, *flags); + __restore_flags(lflags); /* local CPU only */ + spin_lock_irqsave(&io_request_lock, *flags); } return 0; } +/* + * FIXME: This should be changed to enqueue a special request + * to the driver to change settings, and then wait on a sema for completion. + * The current scheme of polling is kludgey, though safe enough. + */ int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val) { unsigned long flags; @@ -2030,7 +2072,7 @@ *p = val; break; } - spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags); + spin_unlock_irqrestore(&io_request_lock, flags); return 0; } @@ -2844,9 +2886,10 @@ return ide_unregister_subdriver(drive); } -static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup) @@ -2884,12 +2927,13 @@ return 0x7fffffff; /* cdrom or tape */ } -static void default_special (ide_drive_t *drive) +static ide_startstop_t default_special (ide_drive_t *drive) { special_t *s = &drive->special; s->all = 0; drive->mult_req = 0; + return ide_stopped; } static void setup_driver_defaults (ide_drive_t *drive) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/ide.h linux.14p2/drivers/block/ide.h --- linux.vanilla/drivers/block/ide.h Sun Mar 28 19:03:35 1999 +++ linux.14p2/drivers/block/ide.h Wed Oct 27 19:23:54 1999 @@ -353,14 +353,22 @@ } ide_hwif_t; /* + * Status returned from various ide_ functions + */ +typedef enum { + ide_stopped, /* no drive operation was started */ + ide_started /* a drive operation was started, and a handler was set up */ +} ide_startstop_t; + +/* * internal ide interrupt handler type */ -typedef void (ide_handler_t)(ide_drive_t *); +typedef ide_startstop_t (ide_handler_t)(ide_drive_t *); typedef struct hwgroup_s { - spinlock_t spinlock; /* protects "busy" and "handler" */ ide_handler_t *handler;/* irq handler, if active */ - int busy; /* BOOL: protects all fields below */ + volatile int busy; /* BOOL: protects all fields below */ + int sleeping; /* BOOL: wake us up on timer expiry */ ide_drive_t *drive; /* current drive */ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */ struct request *rq; /* current request */ @@ -443,13 +451,14 @@ #define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0; #endif + /* * Subdrivers support. */ #define IDE_SUBDRIVER_VERSION 1 typedef int (ide_cleanup_proc)(ide_drive_t *); -typedef void (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long); +typedef ide_startstop_t (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long); typedef void (ide_end_request_proc)(byte, ide_hwgroup_t *); typedef int (ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long); typedef int (ide_open_proc)(struct inode *, struct file *, ide_drive_t *); @@ -457,7 +466,7 @@ typedef int (ide_check_media_change_proc)(ide_drive_t *); typedef void (ide_pre_reset_proc)(ide_drive_t *); typedef unsigned long (ide_capacity_proc)(ide_drive_t *); -typedef void (ide_special_proc)(ide_drive_t *); +typedef ide_startstop_t (ide_special_proc)(ide_drive_t *); typedef void (ide_setting_proc)(ide_drive_t *); typedef struct ide_driver_s { @@ -545,9 +554,9 @@ /* * ide_error() takes action based on the error returned by the controller. - * The calling function must return afterwards, to restart the request. + * The caller should return immediately after invoking this. */ -void ide_error (ide_drive_t *drive, const char *msg, byte stat); +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat); /* * Issue a simple drive command @@ -567,10 +576,11 @@ * This routine busy-waits for the drive status to be not "busy". * It then checks the status for all of the "good" bits and none * of the "bad" bits, and if all is okay it returns 0. All other - * cases return 1 after invoking ide_error() -- caller should return. - * + * cases return 1 after doing "*startstop = ide_error()", and the + * caller should return the updated value of "startstop" in this case. + * "startstop" is unchanged when the function returns 0; */ -int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout); +int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout); /* * This routine is called from the partition-table code in genhd.c @@ -594,7 +604,7 @@ * Start a reset operation for an IDE interface. * The caller should return immediately after invoking this. */ -void ide_do_reset (ide_drive_t *); +ide_startstop_t ide_do_reset (ide_drive_t *); /* * This function is intended to be used prior to invoking ide_do_drive_cmd(). @@ -661,7 +671,7 @@ * ide_multwrite() transfers a block of up to mcount sectors of data * to a drive as part of a disk multwrite operation. */ -void ide_multwrite (ide_drive_t *drive, unsigned int mcount); +int ide_multwrite (ide_drive_t *drive, unsigned int mcount); /* * ide_stall_queue() can be used by a drive to give excess bandwidth back @@ -751,7 +761,7 @@ #define BAD_DMA_DRIVE 0 #define GOOD_DMA_DRIVE 1 int ide_build_dmatable (ide_drive_t *drive); -void ide_dma_intr (ide_drive_t *drive); +ide_startstop_t ide_dma_intr (ide_drive_t *drive); int check_drive_lists (ide_drive_t *drive, int good_bad); int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive); int ide_release_dma (ide_hwif_t *hwif); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/block/pdc4030.c linux.14p2/drivers/block/pdc4030.c --- linux.vanilla/drivers/block/pdc4030.c Tue Dec 29 19:24:57 1998 +++ linux.14p2/drivers/block/pdc4030.c Wed Oct 27 18:56:41 1999 @@ -122,6 +122,7 @@ */ int init_pdc4030 (void) { + ide_startstop_t startstop; ide_hwif_t *hwif = hwif_required; ide_drive_t *drive; ide_hwif_t *second_hwif; @@ -143,7 +144,7 @@ if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { return 0; } - if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + if(ide_wait_stat(&startstop,drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { printk("%s: Failed Promise read config!\n",hwif->name); return 0; } @@ -195,7 +196,7 @@ /* * promise_read_intr() is the handler for disk read/multread interrupts */ -static void promise_read_intr (ide_drive_t *drive) +static ide_startstop_t promise_read_intr (ide_drive_t *drive) { byte stat; int i; @@ -203,8 +204,7 @@ struct request *rq; if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { - ide_error(drive, "promise_read_intr", stat); - return; + return ide_error(drive, "promise_read_intr", stat); } read_again: @@ -240,17 +240,18 @@ goto read_again; if(stat & BUSY_STAT) { ide_set_handler (drive, &promise_read_intr, WAIT_CMD); - return; + return ide_started;; } printk("Ah! promise read intr: sectors left !DRQ !BUSY\n"); - ide_error(drive, "promise read intr", stat); + return ide_error(drive, "promise read intr", stat); } + return ide_stopped; } /* * promise_write_pollfunc() is the handler for disk write completion polling. */ -static void promise_write_pollfunc (ide_drive_t *drive) +static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive) { int i; ide_hwgroup_t *hwgroup = HWGROUP(drive); @@ -259,20 +260,20 @@ if (IN_BYTE(IDE_NSECTOR_REG) != 0) { if (time_before(jiffies, hwgroup->poll_timeout)) { ide_set_handler (drive, &promise_write_pollfunc, 1); - return; /* continue polling... */ + return ide_started; /* continue polling... */ } printk("%s: write timed-out!\n",drive->name); - ide_error (drive, "write timeout", GET_STAT()); - return; + return ide_error (drive, "write timeout", GET_STAT()); } - ide_multwrite(drive, 4); + if (ide_multwrite(drive, 4)) + return ide_stopped; rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } - return; + return ide_stopped; } /* @@ -283,24 +284,27 @@ * how it's done in the drivers for other O/Ses. There is no interrupt * generated on writes, which is why we have to do it like this. */ -static void promise_write (ide_drive_t *drive) +static ide_startstop_t promise_write (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); struct request *rq = &hwgroup->wrq; int i; if (rq->nr_sectors > 4) { - ide_multwrite(drive, rq->nr_sectors - 4); + if (ide_multwrite(drive, rq->nr_sectors - 4)) + return ide_stopped; hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &promise_write_pollfunc, 1); - return; + return ide_started; } else { - ide_multwrite(drive, rq->nr_sectors); + if (ide_multwrite(drive, rq->nr_sectors)) + return ide_stopped; rq = hwgroup->rq; for (i = rq->nr_sectors; i > 0;) { i -= rq->current_nr_sectors; ide_end_request(1, hwgroup); } + return ide_stopped; } } @@ -309,7 +313,7 @@ * already set up. It issues a READ or WRITE command to the Promise * controller, assuming LBA has been used to set up the block number. */ -void do_pdc4030_io (ide_drive_t *drive, struct request *rq) +ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq) { unsigned long timeout; byte stat; @@ -330,28 +334,29 @@ disable_irq(HWIF(drive)->irq); ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL); enable_irq(HWIF(drive)->irq); - return; + return ide_stopped; } if(IN_BYTE(IDE_SELECT_REG) & 0x01) - return; + return ide_started; udelay(1); } while (time_before(jiffies, timeout)); printk("%s: reading: No DRQ and not waiting - Odd!\n", drive->name); - return; + return ide_started; } if (rq->cmd == WRITE) { + ide_startstop_t startstop; OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); - if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name); - return; + return startstop; } if (!drive->unmask) __cli(); /* local CPU only */ HWGROUP(drive)->wrq = *rq; /* scratchpad */ - promise_write(drive); - return; + return promise_write(drive); } printk("%s: bad command: %d\n", drive->name, rq->cmd); ide_end_request(0, HWGROUP(drive)); + return ide_stopped; } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/Makefile linux.14p2/drivers/char/Makefile --- linux.vanilla/drivers/char/Makefile Wed Oct 20 01:12:34 1999 +++ linux.14p2/drivers/char/Makefile Wed Oct 27 18:50:24 1999 @@ -380,9 +380,11 @@ ifeq ($(CONFIG_VIDEO_ZORAN),y) L_OBJS += buz.o +L_I2C=y else ifeq ($(CONFIG_VIDEO_ZORAN),m) M_OBJS += buz.o + M_I2C=y endif endif diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/bttv.c linux.14p2/drivers/char/bttv.c --- linux.vanilla/drivers/char/bttv.c Wed Oct 20 01:12:35 1999 +++ linux.14p2/drivers/char/bttv.c Fri Oct 22 22:16:43 1999 @@ -403,7 +403,7 @@ } } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; @@ -422,7 +422,7 @@ } } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { struct bttv *btv = (struct bttv*)bus->data; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/buz.c linux.14p2/drivers/char/buz.c --- linux.vanilla/drivers/char/buz.c Wed Oct 20 01:12:35 1999 +++ linux.14p2/drivers/char/buz.c Fri Oct 22 22:16:43 1999 @@ -374,13 +374,13 @@ return (btread(ZR36057_I2CBR) >> 1) & 1; } -void attach_inform(struct i2c_bus *bus, int id) +static void attach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id)); } -void detach_inform(struct i2c_bus *bus, int id) +static void detach_inform(struct i2c_bus *bus, int id) { DEBUG(struct zoran *zr = (struct zoran *) bus->data); DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id)); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/radio-aimslab.c linux.14p2/drivers/char/radio-aimslab.c --- linux.vanilla/drivers/char/radio-aimslab.c Sat Aug 14 02:25:39 1999 +++ linux.14p2/drivers/char/radio-aimslab.c Fri Oct 22 22:18:35 1999 @@ -195,7 +195,7 @@ return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/radio-cadet.c linux.14p2/drivers/char/radio-cadet.c --- linux.vanilla/drivers/char/radio-cadet.c Sat Aug 14 02:27:17 1999 +++ linux.14p2/drivers/char/radio-cadet.c Fri Oct 22 22:16:43 1999 @@ -39,6 +39,7 @@ static __u8 rdsin=0,rdsout=0,rdsstat=0; static unsigned char rdsbuf[RDS_BUFFER]; static int cadet_lock=0; +static int cadet_probe(void); /* * Signal Strength Threshold Values diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/char/radio-rtrack2.c linux.14p2/drivers/char/radio-rtrack2.c --- linux.vanilla/drivers/char/radio-rtrack2.c Sat Aug 14 02:25:40 1999 +++ linux.14p2/drivers/char/radio-rtrack2.c Fri Oct 22 22:18:35 1999 @@ -89,7 +89,7 @@ return 0; } -int rt_getsigstr(struct rt_device *dev) +static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ return 0; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/fc4/Makefile linux.14p2/drivers/fc4/Makefile --- linux.vanilla/drivers/fc4/Makefile Tue Mar 16 00:11:29 1999 +++ linux.14p2/drivers/fc4/Makefile Fri Oct 22 21:24:05 1999 @@ -7,8 +7,6 @@ M_OBJS := MOD_LIST_NAME := FC4_MODULES -include ../../.config - ifeq ($(CONFIG_FC4),y) FC4 = fc.o ifeq ($(CONFIG_MODULES),y) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/isdn/avmb1/capi.c linux.14p2/drivers/isdn/avmb1/capi.c --- linux.vanilla/drivers/isdn/avmb1/capi.c Wed Oct 20 01:12:36 1999 +++ linux.14p2/drivers/isdn/avmb1/capi.c Fri Oct 22 22:18:35 1999 @@ -109,6 +109,7 @@ */ #include +#include #include #include #include diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/macintosh/macserial.c linux.14p2/drivers/macintosh/macserial.c --- linux.vanilla/drivers/macintosh/macserial.c Wed Oct 20 01:12:38 1999 +++ linux.14p2/drivers/macintosh/macserial.c Fri Oct 22 22:26:41 1999 @@ -6,7 +6,9 @@ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * - * $Id: macserial.c,v 1.24.2.3 1999/09/10 02:05:58 paulus Exp $ + * Receive DMA code by Takashi Oe . + * + * $Id: macserial.c,v 1.24.2.4 1999/10/19 04:36:42 paulus Exp $ */ #include @@ -28,6 +30,7 @@ #ifdef CONFIG_SERIAL_CONSOLE #include #endif +#include #include #include @@ -42,6 +45,7 @@ #ifdef CONFIG_KGDB #include #endif +#include #include "macserial.h" @@ -53,6 +57,8 @@ }; #endif +#define SUPPORT_SERIAL_DMA + /* * It would be nice to dynamically allocate everything that * depends on NUM_SERIAL, so we could support any number of @@ -128,6 +134,13 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout); static int set_scc_power(struct mac_serial * info, int state); static int setup_scc(struct mac_serial * info); +static void dbdma_reset(volatile struct dbdma_regs *dma); +static void dbdma_flush(volatile struct dbdma_regs *dma); +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs); +static void dma_init(struct mac_serial * info); +static void rxdma_start(struct mac_serial * info, int current); +static void rxdma_to_tty(struct mac_serial * info); static struct tty_struct *serial_table[NUM_CHANNELS]; static struct termios *serial_termios[NUM_CHANNELS]; @@ -153,7 +166,7 @@ __openfirmware #endif /* MODULE */ static inline int serial_paranoia_check(struct mac_serial *info, - dev_t device, const char *routine) + dev_t device, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = @@ -177,7 +190,7 @@ * Reading and writing Z8530 registers. */ static inline unsigned char read_zsreg(struct mac_zschannel *channel, - unsigned char reg) + unsigned char reg) { unsigned char retval; unsigned long flags; @@ -197,7 +210,7 @@ } static inline void write_zsreg(struct mac_zschannel *channel, - unsigned char reg, unsigned char value) + unsigned char reg, unsigned char value) { unsigned long flags; @@ -290,6 +303,39 @@ } /* + * Reset a Descriptor-Based DMA channel. + */ +static void dbdma_reset(volatile struct dbdma_regs *dma) +{ + int i; + + out_le32(&dma->control, (WAKE|FLUSH|PAUSE|RUN) << 16); + + /* + * Yes this looks peculiar, but apparently it needs to be this + * way on some machines. (We need to make sure the DBDMA + * engine has actually got the write above and responded + * to it. - paulus) + */ + for (i = 200; i > 0; --i) + if (ld_le32(&dma->control) & RUN) + udelay(1); +} + +/* + * Tells a DBDMA channel to stop and write any buffered data + * it might have to memory. + */ +static _INLINE_ void dbdma_flush(volatile struct dbdma_regs *dma) +{ + int i = 0; + + out_le32(&dma->control, (FLUSH << 16) | FLUSH); + while (((in_le32(&dma->status) & FLUSH) != 0) && (i++ < 100)) + udelay(1); +} + +/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following @@ -312,6 +358,22 @@ mark_bh(MACSERIAL_BH); } +/* Work out the flag value for a z8530 status value. */ +static _INLINE_ int stat_to_flag(int stat) +{ + int flag; + + if (stat & Rx_OVR) { + flag = TTY_OVERRUN; + } else if (stat & FRM_ERR) { + flag = TTY_FRAME; + } else if (stat & PAR_ERR) { + flag = TTY_PARITY; + } else + flag = 0; + return flag; +} + static _INLINE_ void receive_chars(struct mac_serial *info, struct pt_regs *regs) { @@ -349,14 +411,7 @@ if (flip_max_cnt < tty->flip.count) flip_max_cnt = tty->flip.count; } - if (stat & Rx_OVR) { - flag = TTY_OVERRUN; - } else if (stat & FRM_ERR) { - flag = TTY_FRAME; - } else if (stat & PAR_ERR) { - flag = TTY_PARITY; - } else - flag = 0; + flag = stat_to_flag(stat); if (flag) /* reset the error indication */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -452,6 +507,32 @@ info->read_reg_zero = status; } +static _INLINE_ void receive_special_dma(struct mac_serial *info) +{ + unsigned char stat, flag; + volatile struct dbdma_regs *rd = &info->rx->dma; + int where = RX_BUF_SIZE; + + spin_lock(&info->rx_dma_lock); + if ((ld_le32(&rd->status) & ACTIVE) != 0) + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf] + 1)) + where -= in_le16(&info->rx->res_count); + where--; + + stat = read_zsreg(info->zs_channel, R1); + + flag = stat_to_flag(stat); + if (flag) { + info->rx_flag_buf[info->rx_cbuf][where] = flag; + /* reset the error indication */ + write_zsreg(info->zs_channel, 0, ERR_RES); + } + + spin_unlock(&info->rx_dma_lock); +} + /* * This is the serial driver's generic interrupt routine */ @@ -461,6 +542,12 @@ unsigned char zs_intreg; int shift; + if (!(info->flags & ZILOG_INITIALIZED)) { + printk("rs_interrupt: irq %d, port not initialized\n", irq); + disable_irq(irq); + return; + } + /* NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A @@ -477,19 +564,21 @@ for (;;) { zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift; #ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", irq, (int)zs_intreg); + printk("rs_interrupt: irq %d, zs_intreg 0x%x\n", + irq, (int)zs_intreg); #endif if ((zs_intreg & CHAN_IRQMASK) == 0) break; - if (!(info->flags & ZILOG_INITIALIZED)) { - printk("rs_interrupt: irq %d, port not initialized\n", irq); - break; + if (zs_intreg & CHBRxIP) { + /* If we are doing DMA, we only ask for interrupts + on characters with errors or special conditions. */ + if (info->dma_initted) + receive_special_dma(info); + else + receive_chars(info, regs); } - - if (zs_intreg & CHBRxIP) - receive_chars(info, regs); if (zs_intreg & CHBTxIP) transmit_chars(info); if (zs_intreg & CHBEXT) @@ -497,6 +586,39 @@ } } +/* Transmit DMA interrupt - not used at present */ +static void rs_txdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +/* + * Receive DMA interrupt. + */ +static void rs_rxdma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mac_serial *info = (struct mac_serial *) dev_id; + volatile struct dbdma_cmd *cd; + + if (!info->dma_initted) + return; + spin_lock(&info->rx_dma_lock); + /* First, confirm that this interrupt is, indeed, coming */ + /* from Rx DMA */ + cd = info->rx_cmds[info->rx_cbuf] + 2; + if ((in_le16(&cd->xfer_status) & (RUN | ACTIVE)) != (RUN | ACTIVE)) { + spin_unlock(&info->rx_dma_lock); + return; + } + if (info->rx_fbuf != RX_NO_FBUF) { + info->rx_cbuf = info->rx_fbuf; + if (++info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + } + spin_unlock(&info->rx_dma_lock); +} + /* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. @@ -592,10 +714,6 @@ } } -static void rs_timer(void) -{ -} - static int startup(struct mac_serial * info, int can_sleep) { int delay; @@ -631,6 +749,10 @@ info->flags |= ZILOG_INITIALIZED; enable_irq(info->irq); + if (info->dma_initted) { +// enable_irq(info->tx_dma_irq); + enable_irq(info->rx_dma_irq); + } if (delay) { if (can_sleep) { @@ -644,6 +766,187 @@ return 0; } +static _INLINE_ void rxdma_start(struct mac_serial * info, int current) +{ + volatile struct dbdma_regs *rd = &info->rx->dma; + volatile struct dbdma_cmd *cd = info->rx_cmds[current]; + +//printk(KERN_DEBUG "SCC: rxdma_start\n"); + + st_le32(&rd->cmdptr, virt_to_bus(cd)); + out_le32(&rd->control, (RUN << 16) | RUN); +} + +static void rxdma_to_tty(struct mac_serial *info) +{ + struct tty_struct *tty = info->tty; + volatile struct dbdma_regs *rd = &info->rx->dma; + unsigned long flags; + int residue, available, space, do_queue; + + if (!tty) + return; + + do_queue = 0; + spin_lock_irqsave(&info->rx_dma_lock, flags); +more: + space = TTY_FLIPBUF_SIZE - tty->flip.count; + if (!space) { + do_queue++; + goto out; + } + residue = 0; + if (info->rx_ubuf == info->rx_cbuf) { + if ((ld_le32(&rd->status) & ACTIVE) != 0) { + dbdma_flush(rd); + if (in_le32(&rd->cmdptr) + == virt_to_bus(info->rx_cmds[info->rx_cbuf]+1)) + residue = in_le16(&info->rx->res_count); + } + } + available = RX_BUF_SIZE - residue - info->rx_done_bytes; + if (available > space) + available = space; + if (available) { + memcpy(tty->flip.char_buf_ptr, + info->rx_char_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + memcpy(tty->flip.flag_buf_ptr, + info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + available); + tty->flip.char_buf_ptr += available; + tty->flip.count += available; + tty->flip.flag_buf_ptr += available; + memset(info->rx_flag_buf[info->rx_ubuf] + info->rx_done_bytes, + 0, available); + info->rx_done_bytes += available; + do_queue++; + } + if (info->rx_done_bytes == RX_BUF_SIZE) { + volatile struct dbdma_cmd *cd = info->rx_cmds[info->rx_ubuf]; + + if (info->rx_ubuf == info->rx_cbuf) + goto out; + /* mark rx_char_buf[rx_ubuf] free */ + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le32(&cd->cmd_dep, 0); + st_le32((unsigned int *)&cd->res_count, 0); + cd++; + st_le16(&cd->xfer_status, 0); + + if (info->rx_fbuf == RX_NO_FBUF) { + info->rx_fbuf = info->rx_ubuf; + if (!(ld_le32(&rd->status) & ACTIVE)) { + dbdma_reset(&info->rx->dma); + rxdma_start(info, info->rx_ubuf); + info->rx_cbuf = info->rx_ubuf; + } + } + info->rx_done_bytes = 0; + if (++info->rx_ubuf == info->rx_nbuf) + info->rx_ubuf = 0; + if (info->rx_fbuf == info->rx_ubuf) + info->rx_fbuf = RX_NO_FBUF; + goto more; + } +out: + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + if (do_queue) + queue_task(&tty->flip.tqueue, &tq_timer); +} + +static void poll_rxdma(void *private_) +{ + struct mac_serial *info = (struct mac_serial *) private_; + unsigned long flags; + + rxdma_to_tty(info); + spin_lock_irqsave(&info->rx_dma_lock, flags); + mod_timer(&info->poll_dma_timer, RX_DMA_TIMER); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); +} + +static void dma_init(struct mac_serial * info) +{ + int i, size; + volatile struct dbdma_cmd *cd; + unsigned char *p; + +//printk(KERN_DEBUG "SCC: dma_init\n"); + + info->rx_nbuf = 8; + + /* various mem set up */ + size = sizeof(struct dbdma_cmd) * (3 * info->rx_nbuf + 2) + + (RX_BUF_SIZE * 2 + sizeof(*info->rx_cmds) + + sizeof(*info->rx_char_buf) + sizeof(*info->rx_flag_buf)) + * info->rx_nbuf; + info->dma_priv = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (info->dma_priv == NULL) + return; + memset(info->dma_priv, 0, size); + + info->rx_cmds = (volatile struct dbdma_cmd **)info->dma_priv; + info->rx_char_buf = (unsigned char **) (info->rx_cmds + info->rx_nbuf); + info->rx_flag_buf = info->rx_char_buf + info->rx_nbuf; + p = (unsigned char *) (info->rx_flag_buf + info->rx_nbuf); + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_char_buf[i] = p; + for (i = 0; i < info->rx_nbuf; i++, p += RX_BUF_SIZE) + info->rx_flag_buf[i] = p; + + /* a bit of DMA programming */ + cd = info->rx_cmds[0] = (volatile struct dbdma_cmd *) DBDMA_ALIGN(p); + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[0])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + for (i = 1; i < info->rx_nbuf; i++) { + info->rx_cmds[i] = ++cd; + st_le16(&cd->command, DBDMA_NOP); + cd++; + st_le16(&cd->req_count, RX_BUF_SIZE); + st_le16(&cd->command, INPUT_MORE); + st_le32(&cd->phy_addr, virt_to_bus(info->rx_char_buf[i])); + cd++; + st_le16(&cd->req_count, 4); + st_le16(&cd->command, STORE_WORD | INTR_ALWAYS); + st_le32(&cd->phy_addr, virt_to_bus(cd-2)); + st_le32(&cd->cmd_dep, DBDMA_STOP); + } + cd++; + st_le16(&cd->command, DBDMA_NOP | BR_ALWAYS); + st_le32(&cd->cmd_dep, virt_to_bus(info->rx_cmds[0])); + + /* setup DMA to our liking */ + dbdma_reset(&info->rx->dma); + st_le32(&info->rx->dma.intr_sel, 0x10001); + st_le32(&info->rx->dma.br_sel, 0x10001); + out_le32(&info->rx->dma.wait_sel, 0x10001); + + /* set various flags */ + info->rx_ubuf = 0; + info->rx_cbuf = 0; + info->rx_fbuf = info->rx_ubuf + 1; + if (info->rx_fbuf == info->rx_nbuf) + info->rx_fbuf = RX_NO_FBUF; + info->rx_done_bytes = 0; + + /* setup polling */ + init_timer(&info->poll_dma_timer); + info->poll_dma_timer.function = (void *)&poll_rxdma; + info->poll_dma_timer.data = (unsigned long)info; + + info->dma_initted = 1; +} + static int setup_scc(struct mac_serial * info) { unsigned long flags; @@ -669,6 +972,12 @@ info->xmit_fifo_size = 1; /* + * Reset DMAs + */ + if (info->has_dma) + dma_init(info); + + /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); @@ -682,7 +991,23 @@ /* * Finally, enable sequencing and interrupts */ - info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + if (!info->dma_initted) { + /* interrupt on ext/status changes, all received chars, + transmit ready */ + info->curregs[1] = (info->curregs[1] & ~0x18) + | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); + } else { + /* interrupt on ext/status changes, W/Req pin is + receive DMA request */ + info->curregs[1] = (info->curregs[1] & ~(0x18 | TxINT_ENAB)) + | (EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable W/Req pin */ + info->curregs[1] |= WT_RDY_ENAB; + write_zsreg(info->zs_channel, 1, info->curregs[1]); + /* enable interrupts on transmit ready and receive errors */ + info->curregs[1] |= INT_ERR_Rx | TxINT_ENAB; + } info->pendregs[1] = info->curregs[1]; info->curregs[3] |= (RxENABLE | Rx8); info->pendregs[3] = info->curregs[3]; @@ -708,6 +1033,14 @@ restore_flags(flags); + if (info->dma_initted) { + spin_lock_irqsave(&info->rx_dma_lock, flags); + rxdma_start(info, 0); + info->poll_dma_timer.expires = RX_DMA_TIMER; + add_timer(&info->poll_dma_timer); + spin_unlock_irqrestore(&info->rx_dma_lock, flags); + } + return 0; } @@ -729,7 +1062,14 @@ return; } - + + if (info->has_dma) { + del_timer(&info->poll_dma_timer); + dbdma_reset(info->tx_dma); + dbdma_reset(&info->rx->dma); + disable_irq(info->tx_dma_irq); + disable_irq(info->rx_dma_irq); + } disable_irq(info->irq); info->pendregs[1] = info->curregs[1] = 0; @@ -755,6 +1095,12 @@ info->xmit_buf = 0; } + if (info->has_dma && info->dma_priv) { + kfree(info->dma_priv); + info->dma_priv = NULL; + info->dma_initted = 0; + } + memset(info->curregs, 0, sizeof(info->curregs)); memset(info->curregs, 0, sizeof(info->pendregs)); @@ -1052,7 +1398,6 @@ if (!tty || !info->xmit_buf || !tmp_buf) return 0; - save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { @@ -1068,6 +1413,7 @@ ret = -EFAULT; break; } + save_flags(flags); cli(); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); @@ -1083,6 +1429,7 @@ up(&tmp_buf_sem); } else { while (1) { + save_flags(flags); cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, @@ -1104,7 +1451,6 @@ if (info->xmit_cnt && !tty->stopped && !info->tx_stopped && !info->tx_active) transmit_chars(info); - restore_flags(flags); return ret; } @@ -1133,12 +1479,13 @@ static void rs_flush_buffer(struct tty_struct *tty) { struct mac_serial *info = (struct mac_serial *)tty->driver_data; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; - cli(); + save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); + restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1158,7 +1505,6 @@ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("throttle %ld....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1195,7 +1541,6 @@ struct mac_serial *info = (struct mac_serial *)tty->driver_data; unsigned long flags; #ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; printk("unthrottle %s: %d....\n",tty->ldisc.chars_in_buffer(tty)); #endif @@ -1473,6 +1818,7 @@ save_flags(flags); cli(); if (tty_hung_up_p(filp)) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1498,6 +1844,7 @@ info->count = 0; } if (info->count) { + MOD_DEC_USE_COUNT; restore_flags(flags); return; } @@ -1518,8 +1865,12 @@ printk("waiting end of Tx... (timeout:%d)\n", info->closing_wait); #endif tty->closing = 1; - if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) + if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE) { + restore_flags(flags); tty_wait_until_sent(tty, info->closing_wait); + save_flags(flags); cli(); + } + /* * At this point we stop accepting input. To do this, we * disable the receiver and receive interrupts. @@ -1539,7 +1890,9 @@ #ifdef SERIAL_DEBUG_OPEN printk("waiting end of Rx...\n"); #endif + restore_flags(flags); rs_wait_until_sent(tty, info->timeout); + save_flags(flags); cli(); } shutdown(info); @@ -1565,6 +1918,7 @@ info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; } /* @@ -1603,7 +1957,6 @@ char_time = MIN(char_time, timeout); while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) { current->state = TASK_INTERRUPTIBLE; - current->counter = 0; /* make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; @@ -1775,14 +2128,19 @@ int retval, line; unsigned long page; + MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; - if ((line < 0) || (line >= zs_channels_found)) + if ((line < 0) || (line >= zs_channels_found)) { + MOD_DEC_USE_COUNT; return -ENODEV; + } info = zs_soft + line; #ifdef CONFIG_KGDB - if (info->kgdb_channel) + if (info->kgdb_channel) { + MOD_DEC_USE_COUNT; return -ENODEV; + } #endif if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; @@ -1865,7 +2223,58 @@ static void show_serial_version(void) { - printk("PowerMac Z8530 serial driver version 1.01\n"); + printk("PowerMac Z8530 serial driver version 2.0\n"); +} + +/* + * Initialize one channel, both the mac_serial and mac_zschannel + * structs. We use the dev_node field of the mac_serial struct. + */ +static void +chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan, + struct mac_zschannel *zs_chan_a) +{ + struct device_node *ch = zss->dev_node; + char *conn; + int len; + + zss->irq = ch->intrs[0].line; + zss->has_dma = 0; +#if !defined(CONFIG_KGDB) && defined(SUPPORT_SERIAL_DMA) + if (ch->n_addrs == 3 && ch->n_intrs == 3) + zss->has_dma = 1; +#endif + zss->dma_initted = 0; + + zs_chan->control = (volatile unsigned char *) + ioremap(ch->addrs[0].address, 0x1000); + zs_chan->data = zs_chan->control + 0x10; + spin_lock_init(&zs_chan->lock); + zs_chan->parent = zss; + zss->zs_channel = zs_chan; + zss->zs_chan_a = zs_chan_a; + + /* setup misc varariables */ + zss->kgdb_channel = 0; + zss->is_cobalt_modem = device_is_compatible(ch, "cobalt"); + + /* XXX tested only with wallstreet PowerBook, + should do no harm anyway */ + conn = get_property(ch, "AAPL,connector", &len); + zss->is_pwbk_ir = conn && (strcmp(conn, "infrared") == 0); + + if (zss->has_dma) { + zss->dma_priv = NULL; + /* it seems that the last two addresses are the + DMA controllers */ + zss->tx_dma = (volatile struct dbdma_regs *) + ioremap(ch->addrs[ch->n_addrs - 2].address, 0x100); + zss->rx = (volatile struct mac_dma *) + ioremap(ch->addrs[ch->n_addrs - 1].address, 0x100); + zss->tx_dma_irq = ch->intrs[1].line; + zss->rx_dma_irq = ch->intrs[2].line; + spin_lock_init(&zss->rx_dma_lock); + } } /* Ask the PROM how many Z8530s we have and initialize their zs_channels */ @@ -1874,51 +2283,63 @@ { struct device_node *dev, *ch; struct mac_serial **pp; - int n, lenp; - char *conn; + int n, chip, nchan; + struct mac_zschannel *zs_chan; + int chan_a_index; n = 0; pp = &zs_chain; + zs_chan = zs_channels; for (dev = find_devices("escc"); dev != 0; dev = dev->next) { + nchan = 0; + chip = n; if (n >= NUM_CHANNELS) { printk("Sorry, can't use %s: no more channels\n", dev->full_name); continue; } + chan_a_index = 0; for (ch = dev->child; ch != 0; ch = ch->sibling) { + if (nchan >= 2) { + printk(KERN_WARNING "SCC: Only 2 channels per " + "chip are supported\n"); + break; + } if (ch->n_addrs < 1 || (ch ->n_intrs < 1)) { printk("Can't use %s: %d addrs %d intrs\n", ch->full_name, ch->n_addrs, ch->n_intrs); continue; } - zs_channels[n].control = (volatile unsigned char *) - ioremap(ch->addrs[0].address, 0x1000); - zs_channels[n].data = zs_channels[n].control + 0x10; - spin_lock_init(&zs_channels[n].lock); - zs_soft[n].zs_channel = &zs_channels[n]; - zs_soft[n].dev_node = ch; - zs_soft[n].irq = ch->intrs[0].line; - zs_soft[n].zs_channel->parent = &zs_soft[n]; - zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt"); - - /* XXX tested only with wallstreet PowerBook, - should do no harm anyway */ - conn = get_property(ch, "AAPL,connector", &lenp); - zs_soft[n].is_pwbk_ir = - conn && (strcmp(conn, "infrared") == 0); - - /* XXX this assumes the prom puts chan A before B */ - if (n & 1) - zs_soft[n].zs_chan_a = &zs_channels[n-1]; - else - zs_soft[n].zs_chan_a = &zs_channels[n]; + /* The channel with the higher address + will be the A side. */ + if (nchan > 0 && + ch->addrs[0].address + > zs_soft[n-1].dev_node->addrs[0].address) + chan_a_index = 1; + + /* minimal initialization for now */ + zs_soft[n].dev_node = ch; *pp = &zs_soft[n]; pp = &zs_soft[n].zs_next; + ++nchan; ++n; } + if (nchan == 0) + continue; + + /* set up A side */ + chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan); + ++zs_chan; + + /* set up B side, if it exists */ + if (nchan > 1) + chan_init(&zs_soft[chip + 1 - chan_a_index], + zs_chan, zs_chan - 1); + ++zs_chan; } *pp = 0; + zs_channels_found = n; #ifdef CONFIG_PMAC_PBOOK if (n) @@ -1935,8 +2356,6 @@ /* Setup base handler, and timer table. */ init_bh(MACSERIAL_BH, do_serial_bh); - timer_table[RS_TIMER].fn = rs_timer; - timer_table[RS_TIMER].expires = 0; /* Find out how many Z8530 SCCs we have */ if (zs_chain == 0) @@ -1948,6 +2367,18 @@ /* Register the interrupt handler for each one */ save_flags(flags); cli(); for (i = 0; i < zs_channels_found; ++i) { + if (zs_soft[i].has_dma) { + if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0, + "SCC-txdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].tx_dma_irq); + disable_irq(zs_soft[i].tx_dma_irq); + if (request_irq(zs_soft[i].rx_dma_irq, rs_rxdma_irq, 0, + "SCC-rxdma", &zs_soft[i])) + printk(KERN_ERR "macserial: can't get irq %d\n", + zs_soft[i].rx_dma_irq); + disable_irq(zs_soft[i].rx_dma_irq); + } if (request_irq(zs_soft[i].irq, rs_interrupt, 0, "SCC", &zs_soft[i])) printk(KERN_ERR "macserial: can't get irq %d\n", @@ -2107,8 +2538,13 @@ for (info = zs_chain, i = 0; info; info = info->zs_next, i++) set_scc_power(info, 0); save_flags(flags); cli(); - for (i = 0; i < zs_channels_found; ++i) + for (i = 0; i < zs_channels_found; ++i) { free_irq(zs_soft[i].irq, &zs_soft[i]); + if (zs_soft[i].has_dma) { + free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]); + free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]); + } + } restore_flags(flags); tty_unregister_driver(&callout_driver); tty_unregister_driver(&serial_driver); @@ -2238,6 +2674,8 @@ if (zs_chain == 0) return -1; + set_scc_power(info, 1); + /* Reset the channel */ write_zsreg(info->zs_channel, R9, CHRA); @@ -2481,14 +2919,13 @@ if (zs_chain == 0) probe_sccs(); - set_scc_power(&zs_soft[n], 1); + set_scc_power(&zs_soft[tty_num], 1); zs_kgdbchan = zs_soft[tty_num].zs_channel; zs_soft[tty_num].change_needed = 0; zs_soft[tty_num].clk_divisor = 16; zs_soft[tty_num].zs_baud = 38400; zs_soft[tty_num].kgdb_channel = 1; /* This runs kgdb */ - zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */ /* Turn on transmitter/receiver at 8-bits/char */ kgdb_chaninit(zs_soft[tty_num].zs_channel, 1, 38400); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/macintosh/macserial.h linux.14p2/drivers/macintosh/macserial.h --- linux.vanilla/drivers/macintosh/macserial.h Wed Oct 20 01:12:38 1999 +++ linux.14p2/drivers/macintosh/macserial.h Fri Oct 22 22:26:41 1999 @@ -92,6 +92,13 @@ struct mac_serial* parent; }; +struct mac_dma { + volatile struct dbdma_regs dma; + volatile unsigned short res_count; + volatile unsigned short command; + volatile unsigned int buf_addr; +}; + struct mac_serial { struct mac_serial *zs_next; /* For IRQ servicing chain */ struct mac_zschannel *zs_channel; /* Channel registers */ @@ -156,6 +163,28 @@ struct termios callout_termios; struct wait_queue *open_wait; struct wait_queue *close_wait; + + volatile struct dbdma_regs *tx_dma; + int tx_dma_irq; + volatile struct dbdma_cmd *tx_cmds; + volatile struct mac_dma *rx; + int rx_dma_irq; + volatile struct dbdma_cmd **rx_cmds; + unsigned char **rx_char_buf; + unsigned char **rx_flag_buf; +#define RX_BUF_SIZE 256 + int rx_nbuf; + int rx_done_bytes; + int rx_ubuf; + int rx_fbuf; +#define RX_NO_FBUF (-1) + int rx_cbuf; + spinlock_t rx_dma_lock; + int has_dma; + int dma_initted; + void *dma_priv; + struct timer_list poll_dma_timer; +#define RX_DMA_TIMER (jiffies + 10*HZ/1000) }; @@ -226,9 +255,9 @@ #define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ #define INT_ERR_Rx 0x18 /* Int on error only */ -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ +#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ +#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ +#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ /* Write Register #2 (Interrupt Vector) */ @@ -286,6 +315,9 @@ /* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ +/* Write Register 7' (Some enhanced feature control) */ +#define ENEXREAD 0x40 /* Enable read of some write registers */ + /* Write Register 8 (transmit buffer) */ /* Write Register 9 (Master interrupt control) */ @@ -346,7 +378,9 @@ #define SNRZI 0xe0 /* Set NRZI mode */ /* Write Register 15 (external/status interrupt control) */ +#define EN85C30 1 /* Enable some 85c30-enhanced registers */ #define ZCIE 2 /* Zero count IE */ +#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ #define DCDIE 8 /* DCD IE */ #define SYNCIE 0x10 /* Sync/hunt IE */ #define CTSIE 0x20 /* CTS IE */ @@ -382,6 +416,15 @@ #define END_FR 0x80 /* End of Frame (SDLC) */ /* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x06 /* Read Register 3 (interrupt pending register) ch a only */ #define CHBEXT 0x1 /* Channel B Ext/Stat IP */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/macintosh/mediabay.c linux.14p2/drivers/macintosh/mediabay.c --- linux.vanilla/drivers/macintosh/mediabay.c Wed Oct 20 01:12:38 1999 +++ linux.14p2/drivers/macintosh/mediabay.c Fri Oct 22 22:26:41 1999 @@ -94,6 +94,12 @@ */ #define MB_IDE_WAIT 1500 +/* + * Wait at least this many ticks after resetting an IDE device before + * believing its ready bit. + */ +#define MB_IDE_MINWAIT 250 + static void poll_media_bay(int which); static void set_media_bay(int which, int id); static int media_bay_task(void *); @@ -285,7 +291,10 @@ #endif } #ifdef CONFIG_BLK_DEV_IDE - } else if (bay->cd_timer && (--bay->cd_timer == 0 || MB_IDE_READY(i)) + } else if (bay->cd_timer + && (--bay->cd_timer == 0 + || (bay->cd_timer < MB_IDE_WAIT - MB_IDE_MINWAIT + && MB_IDE_READY(i))) && bay->cd_index < 0) { bay->cd_timer = 0; printk(KERN_DEBUG "Registering IDE, base:0x%08lx, irq:%d\n", bay->cd_base, bay->cd_irq); @@ -299,11 +308,13 @@ } bay->previous_id = bay->content_id; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - if (signal_pending(current)) - return 0; - i = (i+1)%media_bay_count; + if (++i >= media_bay_count) { + i = 0; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + if (signal_pending(current)) + return 0; + } } } @@ -405,25 +416,25 @@ feature_set(bay->dev_node, FEATURE_Mediabay_enable); /* I suppose this is enough delay to stabilize MB_CONTENT ... */ mdelay(10); - /* We re-enable the bay using it's previous content only if - it did not change */ - if (MB_CONTENTS(i) == bay->content_id) { - set_media_bay(i, bay->content_id); - if (bay->content_id != MB_NO) { - mdelay(400); - /* Clear the bay reset */ - feature_clear(bay->dev_node, FEATURE_Mediabay_reset); - /* This small delay makes sure the device has time - to assert the BUSY bit (used by IDE sleep) */ - udelay(100); - /* We reset the state machine timers in case we were in the - middle of a wait loop */ - if (bay->reset_timer) - bay->reset_timer = MB_RESET_COUNT; - if (bay->cd_timer) - bay->cd_timer = MB_IDE_WAIT; - } - } + /* We re-enable the bay using it's previous content + only if it did not change */ + if (MB_CONTENTS(i) != bay->content_id) + continue; + set_media_bay(i, bay->content_id); + if (bay->content_id == MB_NO) + continue; + mdelay(400); + /* Clear the bay reset */ + feature_clear(bay->dev_node, FEATURE_Mediabay_reset); + /* This small delay makes sure the device has time + to assert the BUSY bit (used by IDE sleep) */ + udelay(100); + /* We reset the state machine timers in case we were + in the middle of a wait loop */ + if (bay->reset_timer) + bay->reset_timer = MB_RESET_COUNT; + if (bay->cd_timer) + bay->cd_timer = MB_IDE_WAIT; } break; } diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/misc/parport_pc.c linux.14p2/drivers/misc/parport_pc.c --- linux.vanilla/drivers/misc/parport_pc.c Sat Aug 14 02:27:27 1999 +++ linux.14p2/drivers/misc/parport_pc.c Wed Oct 27 19:05:02 1999 @@ -32,6 +32,9 @@ * only in register addresses (eg because your registers are on 32-bit * word boundaries) then you can alter the constants in parport_pc.h to * accomodate this. + * + * Note that the ECP registers may not start at offset 0x400 for PCI cards, + * but rather will start at port->base_hi. */ #include @@ -43,6 +46,7 @@ #include #include #include +#include #include @@ -62,27 +66,27 @@ void parport_pc_write_epp(struct parport *p, unsigned char d) { - outb(d, p->base+EPPDATA); + outb(d, EPPDATA(p)); } unsigned char parport_pc_read_epp(struct parport *p) { - return inb(p->base+EPPDATA); + return inb(EPPDATA(p)); } void parport_pc_write_epp_addr(struct parport *p, unsigned char d) { - outb(d, p->base+EPPADDR); + outb(d, EPPADDR(p)); } unsigned char parport_pc_read_epp_addr(struct parport *p) { - return inb(p->base+EPPADDR); + return inb(EPPADDR(p)); } int parport_pc_check_epp_timeout(struct parport *p) { - if (!(inb(p->base+STATUS) & 1)) + if (!(inb(STATUS(p)) & 1)) return 0; parport_pc_epp_clear_timeout(p); return 1; @@ -90,24 +94,24 @@ unsigned char parport_pc_read_configb(struct parport *p) { - return inb(p->base+CONFIGB); + return inb(CONFIGB(p)); } void parport_pc_write_data(struct parport *p, unsigned char d) { - outb(d, p->base+DATA); + outb(d, DATA(p)); } unsigned char parport_pc_read_data(struct parport *p) { - return inb(p->base+DATA); + return inb(DATA(p)); } void parport_pc_write_control(struct parport *p, unsigned char d) { struct parport_pc_private *priv = p->private_data; priv->ctr = d;/* update soft copy */ - outb(d, p->base+CONTROL); + outb(d, CONTROL(p)); } unsigned char parport_pc_read_control(struct parport *p) @@ -121,34 +125,34 @@ struct parport_pc_private *priv = p->private_data; unsigned char ctr = priv->ctr; ctr = (ctr & ~mask) ^ val; - outb (ctr, p->base+CONTROL); + outb (ctr, CONTROL(p)); return priv->ctr = ctr; /* update soft copy */ } void parport_pc_write_status(struct parport *p, unsigned char d) { - outb(d, p->base+STATUS); + outb(d, STATUS(p)); } unsigned char parport_pc_read_status(struct parport *p) { - return inb(p->base+STATUS); + return inb(STATUS(p)); } void parport_pc_write_econtrol(struct parport *p, unsigned char d) { - outb(d, p->base+ECONTROL); + outb(d, ECONTROL(p)); } unsigned char parport_pc_read_econtrol(struct parport *p) { - return inb(p->base+ECONTROL); + return inb(ECONTROL(p)); } unsigned char parport_pc_frob_econtrol(struct parport *p, unsigned char mask, unsigned char val) { - unsigned char old = inb(p->base+ECONTROL); - outb(((old & ~mask) ^ val), p->base+ECONTROL); + unsigned char old = inb(ECONTROL(p)); + outb(((old & ~mask) ^ val), ECONTROL(p)); return old; } @@ -159,12 +163,12 @@ void parport_pc_write_fifo(struct parport *p, unsigned char v) { - outb (v, p->base+CONFIGA); + outb (v, CONFIGA(p)); } unsigned char parport_pc_read_fifo(struct parport *p) { - return inb (p->base+CONFIGA); + return inb (CONFIGA(p)); } void parport_pc_disable_irq(struct parport *p) @@ -183,7 +187,7 @@ free_irq(p->irq, p); release_region(p->base, p->size); if (p->modes & PARPORT_MODE_PCECR) - release_region(p->base+0x400, 3); + release_region(p->base_hi, 3); } int parport_pc_claim_resources(struct parport *p) @@ -195,7 +199,7 @@ return err; request_region(p->base, p->size, p->name); if (p->modes & PARPORT_MODE_PCECR) - request_region(p->base+0x400, 3, p->name); + request_region(p->base_hi, 3, p->name); return 0; } @@ -223,8 +227,8 @@ { size_t got = 0; for (; got < length; got++) { - *((char*)buf)++ = inb (p->base+EPPDATA); - if (inb (p->base+STATUS) & 0x01) + *((char*)buf)++ = inb (EPPDATA(p)); + if (inb (STATUS(p)) & 0x01) break; } return got; @@ -234,8 +238,8 @@ { size_t written = 0; for (; written < length; written++) { - outb (*((char*)buf)++, p->base+EPPDATA); - if (inb (p->base+STATUS) & 0x01) + outb (*((char*)buf)++, EPPDATA(p)); + if (inb (STATUS(p)) & 0x01) break; } return written; @@ -370,11 +374,11 @@ * allow reads, so read_control just returns a software * copy. Some ports _do_ allow reads, so bypass the software * copy here. In addition, some bits aren't writable. */ - r = inb (pb->base+CONTROL); + r = inb (CONTROL(pb)); if ((r & 0xf) == w) { w = 0xe; parport_pc_write_control (pb, w); - r = inb (pb->base+CONTROL); + r = inb (CONTROL(pb)); parport_pc_write_control (pb, 0xc); if ((r & 0xf) == w) return PARPORT_MODE_PCSPP; @@ -754,13 +758,16 @@ /* --- Initialisation code -------------------------------- */ -static int probe_one_port(unsigned long int base, int irq, int dma) +static int probe_one_port(unsigned long int base, + unsigned long int base_hi, + int irq, int dma) { struct parport *p; int probedirq = PARPORT_IRQ_NONE; if (check_region(base, 3)) return 0; if (!(p = parport_register_port(base, irq, dma, &parport_pc_ops))) return 0; + p->base_hi = base_hi; p->private_data = kmalloc (sizeof (struct parport_pc_private), GFP_KERNEL); if (!p->private_data) { @@ -770,16 +777,14 @@ return 0; } ((struct parport_pc_private *) (p->private_data))->ctr = 0xc; - if (p->base != 0x3bc) { - if (!check_region(base+0x400,3)) { - p->modes |= parport_ECR_present(p); - p->modes |= parport_ECP_supported(p); - p->modes |= parport_ECPPS2_supported(p); - } - if (!check_region(base+0x3, 5)) { - p->modes |= parport_EPP_supported(p); - p->modes |= parport_ECPEPP_supported(p); - } + if (base_hi && !check_region(base_hi,3)) { + p->modes |= parport_ECR_present(p); + p->modes |= parport_ECP_supported(p); + p->modes |= parport_ECPPS2_supported(p); + } + if (p->base != 0x3bc && !check_region(base+0x3, 5)) { + p->modes |= parport_EPP_supported(p); + p->modes |= parport_ECPEPP_supported(p); } if (!parport_SPP_supported(p)) { /* No port. */ @@ -791,6 +796,8 @@ p->size = (p->modes & (PARPORT_MODE_PCEPP | PARPORT_MODE_PCECPEPP))?8:3; printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base); + if (p->base_hi && (p->modes & PARPORT_MODE_PCECP)) + printk(" (0x%lx)", p->base_hi); if (p->irq == PARPORT_IRQ_AUTO) { p->irq = PARPORT_IRQ_NONE; parport_irq_probe(p); @@ -844,6 +851,124 @@ return 1; } +/* Look for PCI parallel port cards. */ +static int parport_pc_init_pci (int irq, int dma) +{ + struct { + unsigned int vendor; + unsigned int device; + unsigned int numports; + struct { + unsigned long lo; + unsigned long hi; /* -ve if not there */ + } addr[4]; + } cards[] = { + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, 1, + { { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_10x, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_10x, 2, + { { 2, 3 }, { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, 1, + { { 4, 5 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1P_20x, 1, + { { 0, 1 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P_20x, 2, + { { 0, 1 }, { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, 2, + { { 1, 2 }, { 3, 4 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, 1, + { { 1, 2 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, 1, + { { 2, 3 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PARALLEL, 1, + { { 0, -1 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_A, 1, + { { 0, -1 }, } }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DUAL_PAR_B, 1, + { { 0, -1 }, } }, + { 0, } + }; + + struct pci_dev *pcidev; + int count = 0; + int i; + + if (!pci_present ()) + return 0; + + for (i = 0; cards[i].vendor; i++) { + pcidev = NULL; + while ((pcidev = pci_find_device (cards[i].vendor, + cards[i].device, + pcidev)) != NULL) { + int n; + for (n = 0; n < cards[i].numports; n++) { + unsigned long lo = cards[i].addr[n].lo; + unsigned long hi = cards[i].addr[n].hi; + unsigned long io_lo, io_hi; + io_lo = pcidev->base_address[lo]; + io_hi = ((hi < 0) ? 0 : + pcidev->base_address[hi]); + io_lo &= PCI_BASE_ADDRESS_IO_MASK; + io_hi &= PCI_BASE_ADDRESS_IO_MASK; + if (irq == PARPORT_IRQ_AUTO) { + if (probe_one_port (io_lo, + io_hi, + pcidev->irq, + dma)) + count++; + } else if (probe_one_port (io_lo, io_hi, + irq, dma)) + count++; + } + } + } + + /* Look for parallel controllers that we don't know about. */ + for (pcidev = pci_devices; pcidev; pcidev = pcidev->next) { + const int class_noprogif = pcidev->class & ~0xff; + if (class_noprogif != (PCI_CLASS_COMMUNICATION_PARALLEL << 8)) + continue; + + for (i = 0; cards[i].vendor; i++) + if ((cards[i].vendor == pcidev->vendor) && + (cards[i].device == pcidev->device)) + break; + if (cards[i].vendor) + /* We know about this one. */ + continue; + + printk (KERN_INFO + "Unknown PCI parallel I/O card (%04x/%04x)\n" + "Please send 'lspci' output to " + "tim@cyberelk.demon.co.uk\n", + pcidev->vendor, pcidev->device); + } + + return count; +} + int parport_pc_init(int *io, int *irq, int *dma) { int count = 0, i = 0; @@ -851,13 +976,16 @@ /* Only probe the ports we were given. */ user_specified = 1; do { - count += probe_one_port(*(io++), *(irq++), *(dma++)); + unsigned long int io_hi = 0x400 + *io; + count += probe_one_port(*(io++), io_hi, + *(irq++), *(dma++)); } while (*io && (++i < PARPORT_PC_MAX_PORTS)); } else { /* Probe all the likely ports. */ - count += probe_one_port(0x3bc, irq[0], dma[0]); - count += probe_one_port(0x378, irq[0], dma[0]); - count += probe_one_port(0x278, irq[0], dma[0]); + count += probe_one_port(0x3bc, 0x7bc, irq[0], dma[0]); + count += probe_one_port(0x378, 0x778, irq[0], dma[0]); + count += probe_one_port(0x278, 0x678, irq[0], dma[0]); + count += parport_pc_init_pci (irq[0], dma[0]); } return count; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/misc/parport_share.c linux.14p2/drivers/misc/parport_share.c --- linux.vanilla/drivers/misc/parport_share.c Sat Aug 14 02:26:48 1999 +++ linux.14p2/drivers/misc/parport_share.c Wed Oct 27 19:05:02 1999 @@ -108,6 +108,7 @@ tmp->cad_lock = RW_LOCK_UNLOCKED; spin_lock_init(&tmp->waitlist_lock); spin_lock_init(&tmp->pardevice_lock); + tmp->base_hi = base + 0x400; name = kmalloc(15, GFP_KERNEL); if (!name) { diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/Config.in linux.14p2/drivers/net/Config.in --- linux.vanilla/drivers/net/Config.in Wed Oct 20 01:12:38 1999 +++ linux.14p2/drivers/net/Config.in Fri Oct 22 22:16:43 1999 @@ -165,7 +165,9 @@ fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + if [ "$CONFIG_INET" = "y" ]; then + bool 'HIPPI driver support (EXPERIMENTAL)' CONFIG_HIPPI + fi if [ "$CONFIG_HIPPI" = "y" ]; then tristate 'Essential RoadRunner HIPPI PCI adapter support' CONFIG_ROADRUNNER if [ "$CONFIG_ROADRUNNER" != "n" ]; then diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/arlan-proc.c linux.14p2/drivers/net/arlan-proc.c --- linux.vanilla/drivers/net/arlan-proc.c Sat Aug 14 02:27:28 1999 +++ linux.14p2/drivers/net/arlan-proc.c Fri Oct 22 22:16:55 1999 @@ -1,11 +1,11 @@ #include #include "arlan.h" +#include +#include #ifdef CONFIG_PROC_FS -#include -#include /* void enableReceive(struct device* dev); */ @@ -1001,6 +1001,11 @@ {0} }; #endif +#else +static ctl_table arlan_table[MAX_ARLANS + 1] = +{ + {0} +}; #endif static int mmtu = 1234; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/hamradio/Config.in linux.14p2/drivers/net/hamradio/Config.in --- linux.vanilla/drivers/net/hamradio/Config.in Sat Aug 14 02:27:29 1999 +++ linux.14p2/drivers/net/hamradio/Config.in Wed Oct 27 18:50:24 1999 @@ -11,12 +11,12 @@ bool ' support for TRX that feedback the tx signal to rx' CONFIG_SCC_TRXECHO fi -tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX -tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX -dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT -dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT +dep_tristate 'BAYCOM ser12 fullduplex driver for AX.25' CONFIG_BAYCOM_SER_FDX $CONFIG_AX25 +dep_tristate 'BAYCOM ser12 halfduplex driver for AX.25' CONFIG_BAYCOM_SER_HDX $CONFIG_AX25 +dep_tristate 'BAYCOM picpar and par96 driver for AX.25' CONFIG_BAYCOM_PAR $CONFIG_PARPORT $CONFIG_AX25 +dep_tristate 'BAYCOM epp driver for AX.25' CONFIG_BAYCOM_EPP $CONFIG_PARPORT $CONFIG_AX25 -tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM +dep_tristate 'Soundcard modem driver' CONFIG_SOUNDMODEM $CONFIG_AX25 if [ "$CONFIG_SOUNDMODEM" != "n" ]; then bool ' soundmodem support for Soundblaster and compatible cards' CONFIG_SOUNDMODEM_SBC bool ' soundmodem support for WSS and Crystal cards' CONFIG_SOUNDMODEM_WSS @@ -29,4 +29,4 @@ bool ' soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600 fi -tristate 'YAM driver for AX.25' CONFIG_YAM +dep_tristate 'YAM driver for AX.25' CONFIG_YAM $CONFIG_AX25 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/pcnet32.c linux.14p2/drivers/net/pcnet32.c --- linux.vanilla/drivers/net/pcnet32.c Wed Oct 20 01:12:39 1999 +++ linux.14p2/drivers/net/pcnet32.c Fri Oct 22 21:24:24 1999 @@ -13,7 +13,7 @@ * This driver is for PCnet32 and PCnetPCI based ethercards */ -static const char *version = "pcnet32.c:v1.23 6.7.1999 tsbogend@alpha.franken.de\n"; +static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n"; #include #include @@ -39,16 +39,18 @@ #include #include #include +#include static unsigned int pcnet32_portlist[] __initdata = {0x300, 0x320, 0x340, 0x360, 0}; static int pcnet32_debug = 1; +static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ #ifdef MODULE static struct device *pcnet32_dev = NULL; #endif -static const int max_interrupt_work = 20; +static const int max_interrupt_work = 80; static const int rx_copybreak = 200; #define PORT_AUI 0x00 @@ -156,6 +158,12 @@ * Michael Richard ) * added chip id for 79c973/975 (thanks to Zach Brown ) * v1.23 fixed small bug, when manual selecting MII speed/duplex + * v1.24 Applied Thomas' patch to use TxStartPoint and thus decrease TxFIFO + * underflows. Added tx_start_pt module parameter. Increased + * TX_RING_SIZE from 16 to 32. Added #ifdef'd code to use DXSUFLO + * for FAST[+] chipsets. + * v1.24ac Added SMP spinlocking - Alan Cox + * v1.25kf Added No Interrupt on successful Tx for some Tx's */ @@ -166,7 +174,7 @@ */ #ifndef PCNET32_LOG_TX_BUFFERS #define PCNET32_LOG_TX_BUFFERS 4 -#define PCNET32_LOG_RX_BUFFERS 4 +#define PCNET32_LOG_RX_BUFFERS 5 #endif #define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) @@ -255,12 +263,17 @@ struct sk_buff *rx_skbuff[RX_RING_SIZE]; struct pcnet32_access a; void *origmem; - int cur_rx, cur_tx; /* The next free ring entry */ - int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + spinlock_t lock; /* Guard lock */ + 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 net_device_stats stats; char tx_full; int options; int shared_irq:1, /* shared irq possible */ + ltint:1, +#ifdef DO_DXSUFLO + dxsuflo:1, /* disable transmit stop on uflo */ +#endif full_duplex:1, /* full duplex possible */ mii:1; /* mii port available */ #ifdef MODULE @@ -299,6 +312,10 @@ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0, 0, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, pcnet32_probe1}, + { "AMD PCnetPCI series (IBM)", + PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0x1014, 0x2000, + PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, + pcnet32_probe1}, { "AMD PCnetHome series", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_PCNETHOME, 0, 0, PCI_USES_IO|PCI_USES_MASTER, PCNET32_TOTAL_SIZE, @@ -448,8 +465,8 @@ int chip_idx; u16 sdid,svid; - pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &sdid); - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &svid); + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &svid); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sdid); for (chip_idx = 0; pcnet32_tbl[chip_idx].vendor_id; chip_idx++) if ((pdev->vendor == pcnet32_tbl[chip_idx].vendor_id) && (pdev->device == pcnet32_tbl[chip_idx].device_id) && @@ -514,6 +531,10 @@ { struct pcnet32_private *lp; int i,media,fdx = 0, mii = 0; +#ifdef DO_DXSUFLO + int dxsuflo = 0; +#endif + int ltint = 0; int chip_version; char *chipname; char *priv; @@ -532,12 +553,14 @@ return ENODEV; } + chip_version = a->read_csr (ioaddr, 88) | (a->read_csr (ioaddr,89) << 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; + switch (chip_version) { case 0x2420: chipname = "PCnet/PCI 79C970"; @@ -554,11 +577,33 @@ break; case 0x2623: chipname = "PCnet/FAST 79C971"; + /* To prevent Tx FIFO underflows ... (may increase Tx latency) */ + /* Set BCR18:NOUFLO to not start Tx until reach Tx start point */ + /* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */ + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); + /* Set CSR80:XMTSP, Tx start point = 20|64|128|248 bytes or size of frame */ + i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */ + a->write_csr(ioaddr, 80, i | (tx_start << 10)); fdx = 1; mii = 1; +#ifdef DO_DXSUFLO + dxsuflo = 1; +#endif + ltint = 1; break; case 0x2624: chipname = "PCnet/FAST+ 79C972"; + /* To prevent Tx FIFO underflows ... (may increase Tx latency) */ + /* Set BCR18:NOUFLO to not start Tx until reach Tx start point */ + /* Looks like EEPROM sets BCR18:5/6 for BurstWrite/Read */ + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); + /* Set CSR80:XMTSP, Tx start point = 20|64|128|220 bytes or size of frame */ + i = a->read_csr(ioaddr, 80) & ~0x0C00; /* Clear bits we are touching */ + a->write_csr(ioaddr, 80, i | (tx_start << 10)); fdx = 1; mii = 1; +#ifdef DO_DXSUFLO + dxsuflo = 1; +#endif + ltint = 1; break; case 0x2625: chipname = "PCnet/FAST III 79C973"; @@ -602,6 +647,29 @@ for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ + i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ + printk("\n tx_start_pt(0x%04x):",i); + switch(i>>10) { + case 0: printk(" 20 bytes,"); break; + case 1: printk(" 64 bytes,"); break; + case 2: printk(" 128 bytes,"); break; + case 3: printk("~220 bytes,"); break; + } + i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ + printk(" BCR18(%x):",i&0xffff); + if (i & (1<<5)) printk("BurstWrEn "); + if (i & (1<<6)) printk("BurstRdEn "); + if (i & (1<<7)) printk("DWordIO "); + if (i & (1<<11)) printk("NoUFlow "); + i = a->read_bcr(ioaddr, 25); + printk("\n SRAMSIZE=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 26); + printk(" SRAM_BND=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 27); + if (i & (1<<14)) printk("LowLatRx,"); + } + dev->base_addr = ioaddr; request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname); @@ -615,10 +683,17 @@ lp = (struct pcnet32_private *)(((unsigned long)priv+15) & ~15); memset(lp, 0, sizeof(*lp)); + + spin_lock_init(&lp->lock); + dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; lp->full_duplex = fdx; +#ifdef DO_DXSUFLO + lp->dxsuflo = dxsuflo; +#endif + lp->ltint = ltint; lp->mii = mii; if (options[card_idx] > sizeof (options_mapping)) lp->options = PORT_ASEL; @@ -746,7 +821,7 @@ lp->a.write_bcr (ioaddr, 9, val); } - /* set/reset GPSI bit in test register */ + /* NOOP ??? set/reset GPSI bit in test register */ val = lp->a.read_csr (ioaddr, 124) & ~0x10; if ((lp->options & PORT_PORTSEL) == PORT_GPSI) val |= 0x10; @@ -760,6 +835,19 @@ val |= 0x08; lp->a.write_bcr (ioaddr, 32, val); } + +#ifdef DO_DXSUFLO + if (lp->dxsuflo) { /* Disable transmit stop on underflow */ + val = lp->a.read_csr (ioaddr, 3); + val |= 0x40; + lp->a.write_csr (ioaddr, 3, val); + } +#endif + if (lp->ltint) { /* Enable TxDone-intr inhibitor */ + val = lp->a.read_csr (ioaddr, 5); + val |= (1<<14); + lp->a.write_csr (ioaddr, 5, val); + } lp->init_block.mode = le16_to_cpu((lp->options & PORT_PORTSEL) << 7); lp->init_block.filter[0] = 0x00000000; @@ -890,6 +978,7 @@ { struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; unsigned int ioaddr = dev->base_addr; + u16 status; int entry; unsigned long flags; @@ -937,8 +1026,23 @@ return 1; } - save_flags (flags); - cli (); + spin_lock_irqsave(&lp->lock, flags); + + /* Default status -- will not enable Successful-TxDone + * interrupt when that option is available to us. + */ + status = 0x8300; + if ((lp->ltint) && + ((lp->cur_tx - lp->dirty_tx == TX_RING_SIZE/2) || + (lp->cur_tx - lp->dirty_tx >= TX_RING_SIZE-2))) + { + /* Enable Successful-TxDone interrupt if we have + * 1/2 of, or nearly all of, our ring buffer Tx'd + * but not yet cleaned up. Thus, most of the time, + * we will not enable Successful-TxDone interrupts. + */ + status = 0x9300; + } /* Fill in a Tx ring entry */ @@ -954,7 +1058,8 @@ 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->tx_ring[entry].status = le16_to_cpu(status); lp->cur_tx++; lp->stats.tx_bytes += skb->len; @@ -968,7 +1073,7 @@ clear_bit (0, (void *)&dev->tbusy); else lp->tx_full = 1; - restore_flags(flags); + spin_unlock_irqrestore(&lp->lock, flags); return 0; } @@ -990,6 +1095,9 @@ ioaddr = dev->base_addr; lp = (struct pcnet32_private *)dev->priv; + + spin_lock(&lp->lock); + if (dev->interrupt) printk("%s: Re-entering the interrupt handler.\n", dev->name); @@ -1010,7 +1118,7 @@ pcnet32_rx(dev); if (csr0 & 0x0200) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; + unsigned int dirty_tx = lp->dirty_tx; while (dirty_tx < lp->cur_tx) { int entry = dirty_tx & TX_RING_MOD_MASK; @@ -1028,14 +1136,27 @@ 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++; +#ifndef DO_DXSUFLO if (err_status & 0x40000000) { - /* Ackk! On FIFO errors the Tx unit is turned off! */ lp->stats.tx_fifo_errors++; + /* Ackk! On FIFO errors the Tx unit is turned off! */ /* Remove this verbosity later! */ - printk("%s: Tx FIFO error! Status %4.4x.\n", - dev->name, csr0); + printk("%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); must_restart = 1; } +#else + if (err_status & 0x40000000) { + lp->stats.tx_fifo_errors++; + if (! lp->dxsuflo) { /* If controller doesn't recover ... */ + /* Ackk! On FIFO errors the Tx unit is turned off! */ + /* Remove this verbosity later! */ + printk("%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); + must_restart = 1; + } + } +#endif } else { if (status & 0x1800) lp->stats.collisions++; @@ -1104,6 +1225,8 @@ dev->name, lp->a.read_csr (ioaddr, 0)); dev->interrupt = 0; + + spin_unlock(&lp->lock); return; } @@ -1252,12 +1375,11 @@ u16 saved_addr; unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&lp->lock, flags); saved_addr = lp->a.read_rap(ioaddr); lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112); lp->a.write_rap(ioaddr, saved_addr); - restore_flags(flags); + spin_unlock_irqrestore(&lp->lock, flags); return &lp->stats; } @@ -1371,18 +1493,22 @@ MODULE_PARM(debug, "i"); MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(tx_start_pt, "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); /* An additional parameter that may be passed in... */ static int debug = -1; +static int tx_start_pt = -1; int init_module(void) { if (debug > 0) pcnet32_debug = debug; + if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) + tx_start = tx_start_pt; pcnet32_dev = NULL; return pcnet32_probe(NULL); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/sis900.c linux.14p2/drivers/net/sis900.c --- linux.vanilla/drivers/net/sis900.c Wed Oct 20 01:12:39 1999 +++ linux.14p2/drivers/net/sis900.c Fri Oct 22 22:25:35 1999 @@ -1,58 +1,28 @@ -/*****************************************************************************/ -/* sis900.c: A SiS 900 PCI Fast Ethernet driver for Linux. */ -/* */ -/* Silicon Integrated System Corporation */ -/* Revision: 1.05 Aug 7 1999 */ -/* */ -/*****************************************************************************/ - -/* - Modified from the driver which is originally written by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU Public License (GPL), incorporated herein by reference. - Drivers based on this skeleton fall under the GPL and must retain - the authorship (implicit copyright) notice. - - The author may be reached as becker@tidalwave.net, or - Donald Becker - 312 Severn Ave. #W302 - Annapolis MD 21403 - - Support and updates [to the original skeleton] available at - http://www.tidalwave.net/~becker/pci-skeleton.html +/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. + Silicon Integrated System Corporation + Revision: 1.05 Aug 7 1999 + + Modified from the driver which is originally written by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + Drivers based on this skeleton fall under the GPL and must retain + the authorship (implicit copyright) notice. + + References: + SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + preliminary Rev. 1.0 Jan. 14, 1998 + SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + preliminary Rev. 1.0 Nov. 10, 1998 + SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + preliminary Rev. 1.0 Jan. 18, 1998 + http://www.sis.com.tw/support/databook.htm + + Ollie Lho (ollie@sis.com.tw) + Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support + Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release */ -static const char *version = -"sis900.c:v1.05 8/07/99\n"; - -static int max_interrupt_work = 20; -#define sis900_debug debug -static int sis900_debug = 0; - -static int multicast_filter_limit = 128; - -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int speeds[MAX_UNITS] = {100, 100, 100, 100, 100, 100, 100, 100}; -static int full_duplex[MAX_UNITS] = {1, 1, 1, 1, 1, 1, 1, 1}; - -#define TX_BUF_SIZE 1536 -#define RX_BUF_SIZE 1536 - -#define TX_DMA_BURST 0 -#define RX_DMA_BURST 0 -#define TX_FIFO_THRESH 16 -#define TxDRNT_100 (1536>>5) -#define TxDRNT_10 16 -#define RxDRNT_100 8 -#define RxDRNT_10 8 -#define TRUE 1 -#define FALSE 0 - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (4*HZ) - #include #include #include @@ -70,293 +40,59 @@ #include /* Processor type for cache alignment. */ #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 -#if LINUX_VERSION_CODE <= 0x20139 -#define net_device_stats enet_statistics -#else -#define NETSTATS_VER2 -#endif -#if LINUX_VERSION_CODE < 0x20155 || defined(CARDBUS) -/* Grrrr, the PCI code changed, but did not consider CardBus... */ -#include -#define PCI_SUPPORT_VER1 -#else -#define PCI_SUPPORT_VER2 -#endif -#if LINUX_VERSION_CODE < 0x20159 -#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); -#else -#define dev_free_skb(skb) dev_kfree_skb(skb); -#endif +#include "sis900.h" -/* The I/O extent. */ -#define SIS900_TOTAL_SIZE 0x100 +static const char *version = +"sis900.c:v1.05 8/07/99\n"; -/* This table drives the PCI probe routines. It's mostly boilerplate in all - of the drivers, and will likely be provided by some future kernel. - Note the matching code -- the first table entry matchs all 56** cards but - second only the 1234 card. -*/ +static int max_interrupt_work = 20; +#define sis900_debug debug +static int sis900_debug = 0; -enum pci_flags_bit { - PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, -}; +static int multicast_filter_limit = 128; + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*HZ) -struct pci_id_info { +struct mac_chip_info { const char *name; - u16 vendor_id, device_id, device_id_mask, flags; - int io_size; - struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, - long ioaddr, int irq, int chip_idx, int fnd_cnt); + u16 vendor_id, device_id, flags; + int io_size; + struct device *(*probe) (struct mac_chip_info *mac, struct pci_dev * pci_dev, + struct device * net_dev); }; +static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev); -static struct device * sis900_probe1(int pci_bus, int pci_devfn, - struct device *dev, long ioaddr, - int irq, int chp_idx, int fnd_cnt); - -static struct pci_id_info pci_tbl[] = -{{ "SiS 900 PCI Fast Ethernet", - 0x1039, 0x0900, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, - { "SiS 7016 PCI Fast Ethernet", - 0x1039, 0x7016, 0xffff, PCI_USES_IO|PCI_USES_MASTER, 0x100, sis900_probe1}, - {0,}, /* 0 terminated list. */ +static struct mac_chip_info mac_chip_table[] = { + { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, + { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, + {0,}, /* 0 terminated list. */ }; -/* The capability table matches the chip table above. */ -enum {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, HAS_LNK_CHNG=0x04}; -static int sis_cap_tbl[] = { - HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, - HAS_MII_XCVR|HAS_CHIP_XCVR|HAS_LNK_CHNG, +static struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; +} mii_chip_table[] = { + {"SiS 900 Internal MII PHY", 0x001d, 0x8000}, + {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8}, + {0,}, }; -/* The rest of these values should never change. */ -#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ -#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */ - -/* Symbolic offsets to registers. */ -enum SIS900_registers { - cr=0x0, //Command Register - cfg=0x4, //Configuration Register - mear=0x8, //EEPROM Access Register - ptscr=0xc, //PCI Test Control Register - isr=0x10, //Interrupt Status Register - imr=0x14, //Interrupt Mask Register - ier=0x18, //Interrupt Enable Register - epar=0x18, //Enhanced PHY Access Register - txdp=0x20, //Transmit Descriptor Pointer Register - txcfg=0x24, //Transmit Configuration Register - rxdp=0x30, //Receive Descriptor Pointer Register - rxcfg=0x34, //Receive Configuration Register - flctrl=0x38, //Flow Control Register - rxlen=0x3c, //Receive Packet Length Register - rfcr=0x48, //Receive Filter Control Register - rfdr=0x4C, //Receive Filter Data Register - pmctrl=0xB0, //Power Management Control Register - pmer=0xB4 //Power Management Wake-up Event Register +struct mii_phy { + struct mii_phy * next; + struct mii_chip_info * chip_info; + int phy_addr; + u16 status; }; -#define RESET 0x00000100 -#define SWI 0x00000080 -#define RxRESET 0x00000020 -#define TxRESET 0x00000010 -#define RxDIS 0x00000008 -#define RxENA 0x00000004 -#define TxDIS 0x00000002 -#define TxENA 0x00000001 - -#define BISE 0x80000000 -#define EUPHCOM 0x00000100 -#define REQALG 0x00000080 -#define SB 0x00000040 -#define POW 0x00000020 -#define EXD 0x00000010 -#define PESEL 0x00000008 -#define LPM 0x00000004 -#define BEM 0x00000001 - -/* Interrupt register bits, using my own meaningful names. */ -#define WKEVT 0x10000000 -#define TxPAUSEEND 0x08000000 -#define TxPAUSE 0x04000000 -#define TxRCMP 0x02000000 -#define RxRCMP 0x01000000 -#define DPERR 0x00800000 -#define SSERR 0x00400000 -#define RMABT 0x00200000 -#define RTABT 0x00100000 -#define RxSOVR 0x00010000 -#define HIBERR 0x00008000 -#define SWINT 0x00001000 -#define MIBINT 0x00000800 -#define TxURN 0x00000400 -#define TxIDLE 0x00000200 -#define TxERR 0x00000100 -#define TxDESC 0x00000080 -#define TxOK 0x00000040 -#define RxORN 0x00000020 -#define RxIDLE 0x00000010 -#define RxEARLY 0x00000008 -#define RxERR 0x00000004 -#define RxDESC 0x00000002 -#define RxOK 0x00000001 - -#define IE 0x00000001 - -#define TxCSI 0x80000000 -#define TxHBI 0x40000000 -#define TxMLB 0x20000000 -#define TxATP 0x10000000 -#define TxIFG 0x0C000000 -#define TxMXF 0x03800000 -#define TxMXF_shift 0x23 -#define TxMXDMA 0x00700000 -#define TxMXDMA_shift 20 -#define TxRTCNT 0x000F0000 -#define TxRTCNT_shift 16 -#define TxFILLT 0x00007F00 -#define TxFILLT_shift 8 -#define TxDRNT 0x0000007F - -#define RxAEP 0x80000000 -#define RxARP 0x40000000 -#define RxATP 0x10000000 -#define RxAJAB 0x08000000 -#define RxMXF 0x03800000 -#define RxMXF_shift 23 -#define RxMXDMA 0x00700000 -#define RxMXDMA_shift 20 -#define RxDRNT 0x0000007F - -#define RFEN 0x80000000 -#define RFAAB 0x40000000 -#define RFAAM 0x20000000 -#define RFAAP 0x10000000 -#define RFPromiscuous (RFAAB|RFAAM|RFAAP) -#define RFAA_shift 28 -#define RFEP 0x00070000 -#define RFEP_shift 16 - -#define RFDAT 0x0000FFFF - -#define OWN 0x80000000 -#define MORE 0x40000000 -#define INTR 0x20000000 -#define OK 0x08000000 -#define DSIZE 0x00000FFF - -#define SUPCRC 0x10000000 -#define ABORT 0x04000000 -#define UNDERRUN 0x02000000 -#define NOCARRIER 0x01000000 -#define DEFERD 0x00800000 -#define EXCDEFER 0x00400000 -#define OWCOLL 0x00200000 -#define EXCCOLL 0x00100000 -#define COLCNT 0x000F0000 - -#define INCCRC 0x10000000 -// ABORT 0x04000000 -#define OVERRUN 0x02000000 -#define DEST 0x01800000 -#define BCAST 0x01800000 -#define MCAST 0x01000000 -#define UNIMATCH 0x00800000 -#define TOOLONG 0x00400000 -#define RUNT 0x00200000 -#define RXISERR 0x00100000 -#define CRCERR 0x00080000 -#define FAERR 0x00040000 -#define LOOPBK 0x00020000 -#define RXCOL 0x00010000 - -#define EuphLiteEEMACAddr 0x08 -#define EuphLiteEEVendorID 0x02 -#define EuphLiteEEDeviceID 0x03 -#define EuphLiteEECardTypeRev 0x0b -#define EuphLiteEEPlexusRev 0x0c -#define EuphLiteEEChecksum 0x0f - -#define RXSTS_shift 18 -#define OWN 0x80000000 -#define MORE 0x40000000 -#define INTR 0x20000000 -#define OK 0x08000000 -#define DSIZE 0x00000FFF -/* MII register offsets */ -#define MII_CONTROL 0x0000 -#define MII_STATUS 0x0001 -#define MII_PHY_ID0 0x0002 -#define MII_PHY_ID1 0x0003 -#define MII_ANAR 0x0004 -#define MII_ANLPAR 0x0005 -#define MII_ANER 0x0006 -/* MII Control register bit definitions. */ -#define MIICNTL_FDX 0x0100 -#define MIICNTL_RST_AUTO 0x0200 -#define MIICNTL_ISOLATE 0x0400 -#define MIICNTL_PWRDWN 0x0800 -#define MIICNTL_AUTO 0x1000 -#define MIICNTL_SPEED 0x2000 -#define MIICNTL_LPBK 0x4000 -#define MIICNTL_RESET 0x8000 -/* MII Status register bit significance. */ -#define MIISTAT_EXT 0x0001 -#define MIISTAT_JAB 0x0002 -#define MIISTAT_LINK 0x0004 -#define MIISTAT_CAN_AUTO 0x0008 -#define MIISTAT_FAULT 0x0010 -#define MIISTAT_AUTO_DONE 0x0020 -#define MIISTAT_CAN_T 0x0800 -#define MIISTAT_CAN_T_FDX 0x1000 -#define MIISTAT_CAN_TX 0x2000 -#define MIISTAT_CAN_TX_FDX 0x4000 -#define MIISTAT_CAN_T4 0x8000 -/* MII NWAY Register Bits ... -** valid for the ANAR (Auto-Negotiation Advertisement) and -** ANLPAR (Auto-Negotiation Link Partner) registers */ -#define MII_NWAY_NODE_SEL 0x001f -#define MII_NWAY_CSMA_CD 0x0001 -#define MII_NWAY_T 0x0020 -#define MII_NWAY_T_FDX 0x0040 -#define MII_NWAY_TX 0x0080 -#define MII_NWAY_TX_FDX 0x0100 -#define MII_NWAY_T4 0x0200 -#define MII_NWAY_RF 0x2000 -#define MII_NWAY_ACK 0x4000 -#define MII_NWAY_NP 0x8000 - -/* MII Auto-Negotiation Expansion Register Bits */ -#define MII_ANER_PDF 0x0010 -#define MII_ANER_LP_NP_ABLE 0x0008 -#define MII_ANER_NP_ABLE 0x0004 -#define MII_ANER_RX_PAGE 0x0002 -#define MII_ANER_LP_AN_ABLE 0x0001 -#define HALF_DUPLEX 1 -#define FDX_CAPABLE_DUPLEX_UNKNOWN 2 -#define FDX_CAPABLE_HALF_SELECTED 3 -#define FDX_CAPABLE_FULL_SELECTED 4 -#define HW_SPEED_UNCONFIG 0 -#define HW_SPEED_10_MBPS 10 -#define HW_SPEED_100_MBPS 100 -#define HW_SPEED_DEFAULT (HW_SPEED_10_MBPS) - -#define ACCEPT_ALL_PHYS 0x01 -#define ACCEPT_ALL_MCASTS 0x02 -#define ACCEPT_ALL_BCASTS 0x04 -#define ACCEPT_ALL_ERRORS 0x08 -#define ACCEPT_CAM_QUALIFIED 0x10 -#define MAC_LOOPBACK 0x20 -//#define FDX_CAPABLE_FULL_SELECTED 4 -#define CRC_SIZE 4 -#define MAC_HEADER_SIZE 14 - typedef struct _EuphLiteDesc { u32 llink; unsigned char* buf; @@ -368,19 +104,15 @@ } EuphLiteDesc; struct sis900_private { - char devname[8]; /* Used only for kernel debugging. */ - const char *product_name; struct device *next_module; - int chip_id; - int chip_revision; - unsigned char pci_bus, pci_devfn; -#if LINUX_VERSION_CODE > 0x20139 struct net_device_stats stats; -#else - struct enet_statistics stats; -#endif - struct timer_list timer; /* Media selection timer. */ - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ + struct pci_dev * pci_dev; + + struct mac_chip_info * mac; + struct mii_phy * mii; + + struct timer_list timer; /* Media selection timer. */ + unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ unsigned int cur_tx, dirty_tx, tx_flag; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ @@ -388,15 +120,12 @@ EuphLiteDesc tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ EuphLiteDesc rx_buf[NUM_RX_DESC]; unsigned char *rx_bufs; - unsigned char *tx_bufs; /* Tx bounce buffer region. */ - char phys[4]; /* MII device addresses. */ - int phy_idx; /* Support Max 4 PHY */ - u16 pmd_status; - unsigned int tx_full; /* The Tx queue is full. */ - int MediaSpeed; /* user force speed */ - int MediaDuplex; /* user force duplex */ - int full_duplex; /* Full/Half-duplex. */ - int speeds; /* 100/10 Mbps. */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + unsigned int tx_full; /* The Tx queue is full. */ + int MediaSpeed; /* user force speed */ + int MediaDuplex; /* user force duplex */ + int full_duplex; /* Full/Half-duplex. */ + int speeds; /* 100/10 Mbps. */ u16 LinkOn; u16 LinkChange; }; @@ -414,8 +143,10 @@ #endif static int sis900_open(struct device *dev); +static int sis900_mii_probe (struct device * dev); +static void sis900_init_rxfilter (struct device * dev); static u16 read_eeprom(long ioaddr, int location); -static int mdio_read(struct device *dev, int phy_id, int location); +static u16 mdio_read(struct device *dev, int phy_id, int location); static void mdio_write(struct device *dev, int phy_id, int location, int val); static void sis900_timer(unsigned long data); static void sis900_tx_timeout(struct device *dev); @@ -431,314 +162,199 @@ static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed); static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed); static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex); -static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, u16 polarity, u16 *value); +static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, + u16 polarity, u16 *value); static void elSetMediaType(struct device *dev, int speed, int duplex); /* A list of all installed SiS900 devices, for removing the driver module. */ static struct device *root_sis900_dev = NULL; -/* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well when dynamically adding drivers. So instead we detect just the - SiS 900 cards in slot order. */ - -int sis900_probe(struct device *dev) -{ - int cards_found = 0; - int pci_index = 0; - unsigned char pci_bus, pci_device_fn; - - if ( ! pcibios_present()) - return -ENODEV; - - for (;pci_index < 0xff; pci_index++) { - u16 vendor, device, pci_command, new_command; - int chip_idx, irq; - long ioaddr; - - if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, - pci_index, - &pci_bus, &pci_device_fn) - != PCIBIOS_SUCCESSFUL) { - break; - } - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, - &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, - &device); - - for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) - if (vendor == pci_tbl[chip_idx].vendor_id && - (device & pci_tbl[chip_idx].device_id_mask) == - pci_tbl[chip_idx].device_id) - break; - if (pci_tbl[chip_idx].vendor_id == 0) /* Compiled out! */ - continue; +/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/ +int sis900_probe (struct device * net_dev) +{ + int found = 0; + struct pci_dev * pci_dev = NULL; + + if (!pci_present()) + return -ENODEV; + + while ((pci_dev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_dev)) != NULL) { + /* pci_dev contains all ethernet devices */ + u32 pci_io_base; + struct mac_chip_info * mac; + + for (mac = mac_chip_table; mac->vendor_id; mac++) { + /* try to match our card list */ + if (pci_dev->vendor == mac->vendor_id && + pci_dev->device == mac->device_id) + break; + } + + if (mac->vendor_id == 0) + /* pci_dev does not match any of our cards */ + continue; + + /* now, pci_dev should be either 900 or 7016 */ + pci_io_base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; + if ((mac->flags & PCI_COMMAND_IO ) && + check_region(pci_io_base, mac->io_size)) + continue; + + /* setup various bits in PCI command register */ + pci_set_master(pci_dev); + + /* do the real low level jobs */ + net_dev = mac->probe(mac, pci_dev, net_dev); + + if (net_dev != NULL) { + found++; + } + net_dev = NULL; + } + return found ? 0 : -ENODEV; +} - { -#if defined(PCI_SUPPORT_VER2) - struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); - ioaddr = pdev->base_address[0] & ~3; - irq = pdev->irq; -#else - u32 pci_ioaddr; - u8 pci_irq_line; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); - ioaddr = pci_ioaddr & ~3; - irq = pci_irq_line; -#endif - } +static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_dev * pci_dev, + struct device * net_dev) +{ + struct sis900_private *sis_priv; + long ioaddr = pci_dev->base_address[0] & ~3; + int irq = pci_dev->irq; + static int did_version = 0; + u16 signature; + int i; + + if (did_version++ == 0) + printk(KERN_INFO "%s", version); + + /* check to see if we have sane EEPROM */ + signature = (u16) read_eeprom(ioaddr, EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printk (KERN_INFO "Error EERPOM read %x\n", signature); + return NULL; + } - if ((pci_tbl[chip_idx].flags & PCI_USES_IO) && - check_region(ioaddr, pci_tbl[chip_idx].io_size)) - continue; - - /* Activate the card: fix for brain-damaged Win98 BIOSes. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - new_command = pci_command | (pci_tbl[chip_idx].flags & 7); - if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled the" - " device at %d/%d!" - "Updating PCI command %4.4x->%4.4x.\n", - pci_bus, pci_device_fn, - pci_command, new_command); + if ((net_dev = init_etherdev(net_dev, 0)) == NULL) + return NULL; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, new_command); - } + printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name, + ioaddr, irq); - dev = pci_tbl[chip_idx].probe1(pci_bus, - pci_device_fn, - dev, - ioaddr, - irq, - chip_idx, - cards_found); - - if (dev && (pci_tbl[chip_idx].flags & PCI_COMMAND_MASTER)) { - u8 pci_latency; - - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - - if (pci_latency < 32) { - printk(KERN_NOTICE " PCI latency timer (CFLT) is " - "unreasonably low at %d. Setting to 64 clocks.\n", - pci_latency); - pcibios_write_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, 64); - } - } - dev = 0; - cards_found++; - } - return cards_found ? 0 : -ENODEV; -} + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr); + for (i = 0; i < 5; i++) + printk("%2.2x:", (u8)net_dev->dev_addr[i]); + printk("%2.2x.\n", net_dev->dev_addr[i]); + + if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) + /* FIXME: possible mem leak here */ + return NULL; + + sis_priv = net_dev->priv; + memset(sis_priv, 0, sizeof(struct sis900_private)); + + /* We do a request_region() to register /proc/ioports info. */ + request_region(ioaddr, mac->io_size, net_dev->name); + net_dev->base_addr = ioaddr; + net_dev->irq = irq; + sis_priv->pci_dev = pci_dev; + sis_priv->mac = mac; + + /* probe for mii transciver */ + if (sis900_mii_probe(net_dev) == 0) { + /* FIXME: how to clean up this */ + release_region (ioaddr, mac->io_size); + return NULL; + } -static struct device * sis900_probe1( int pci_bus, - int pci_devfn, - struct device *dev, - long ioaddr, - int irq, - int chip_idx, - int found_cnt) -{ - static int did_version = 0; /* Already printed version info. */ - struct sis900_private *tp; - u16 status; - int duplex = found_cnt < MAX_UNITS ? full_duplex[found_cnt] : 0 ; - int speed = found_cnt < MAX_UNITS ? speeds[found_cnt] : 0 ; - int phy=0, phy_idx=0, i; - - if (did_version++ == 0) - printk(KERN_INFO "%s", version); - - dev = init_etherdev(dev, 0); - - if(dev==NULL) - return NULL; - - printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", - dev->name, pci_tbl[chip_idx].name, ioaddr, irq); - - if ((u16)read_eeprom(ioaddr, EuphLiteEEVendorID) != 0xffff) { - for (i = 0; i < 3; i++) - ((u16 *)(dev->dev_addr))[i] = - read_eeprom(ioaddr,i+EuphLiteEEMACAddr); - for (i = 0; i < 5; i++) - printk("%2.2x:", (u8)dev->dev_addr[i]); - printk("%2.2x.\n", dev->dev_addr[i]); - } else - printk(KERN_INFO "Error EEPROM read\n"); - - /* We do a request_region() to register /proc/ioports info. */ - request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); - - dev->base_addr = ioaddr; - dev->irq = irq; - - /* Some data structures must be quadword aligned. */ - tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA); - if(tp==NULL) - { - release_region(ioaddr, pci_tbl[chip_idx].io_size); - return NULL; - } - memset(tp, 0, sizeof(*tp)); - dev->priv = tp; - - tp->next_module = root_sis900_dev; - root_sis900_dev = dev; - - tp->chip_id = chip_idx; - tp->pci_bus = pci_bus; - tp->pci_devfn = pci_devfn; - - /* Find the connected MII xcvrs. - Doing this in open() would allow detecting external xcvrs later, but - takes too much time. */ - if (sis_cap_tbl[chip_idx] & HAS_MII_XCVR) { - for (phy = 0, phy_idx = 0; - phy < 32 && phy_idx < sizeof(tp->phys); phy++) - { - int mii_status ; - mii_status = mdio_read(dev, phy, MII_STATUS); - - if (mii_status != 0xffff && mii_status != 0x0000) { - tp->phy_idx = phy_idx; - tp->phys[phy_idx++] = phy; - tp->pmd_status=mdio_read(dev, phy, MII_STATUS); - printk(KERN_INFO "%s: MII transceiver found " - "at address %d.\n", - dev->name, phy); - break; - } - } + sis_priv->next_module = root_sis900_dev; + root_sis900_dev = net_dev; - if (phy_idx == 0) { - printk(KERN_INFO "%s: No MII transceivers found!\n", - dev->name); - tp->phys[0] = -1; - tp->pmd_status = 0; - } - } else { - tp->phys[0] = -1; - tp->pmd_status = 0; - } - - if ((tp->pmd_status > 0) && (phy_idx > 0)) { - if (sis900_debug > 1) { - printk(KERN_INFO "duplex=%d, speed=%d\n", - duplex, speed); - } - if (!duplex && !speed) { - // auto-config media type - // Set full capability - if (sis900_debug > 1) { - printk(KERN_INFO "Auto Config ...\n"); - } - elSetCapability(dev, tp->phys[tp->phy_idx], 1, 100); - tp->pmd_status=elAutoNegotiate(dev, - tp->phys[tp->phy_idx], - &tp->full_duplex, - &tp->speeds); - } else { - tp->MediaSpeed = speed; - tp->MediaDuplex = duplex; - elSetCapability(dev, tp->phys[tp->phy_idx], - duplex, speed); - elAutoNegotiate(dev, tp->phys[tp->phy_idx], - &tp->full_duplex, - &tp->speeds); - status = mdio_read(dev, phy, MII_ANLPAR); - if ( !(status & (MII_NWAY_T | MII_NWAY_T_FDX | - MII_NWAY_TX | MII_NWAY_TX_FDX ))) - { - u16 cmd=0; - cmd |= ( speed == 100 ? - MIICNTL_SPEED : 0 ); - cmd |= ( duplex ? MIICNTL_FDX : 0 ); - mdio_write(dev, phy, MII_CONTROL, cmd); - elSetMediaType(dev, speed==100 ? - HW_SPEED_100_MBPS : - HW_SPEED_10_MBPS, - duplex ? - FDX_CAPABLE_FULL_SELECTED: - FDX_CAPABLE_HALF_SELECTED); - elMIIpollBit(dev, phy, MII_STATUS, - MIISTAT_LINK, TRUE, &status); - } else { - status = mdio_read(dev, phy, MII_STATUS); - } - } + /* The SiS900-specific entries in the device structure. */ + net_dev->open = &sis900_open; + net_dev->hard_start_xmit = &sis900_start_xmit; + net_dev->stop = &sis900_close; + net_dev->get_stats = &sis900_get_stats; + net_dev->set_multicast_list = &set_rx_mode; + net_dev->do_ioctl = &mii_ioctl; - if (tp->pmd_status & MIISTAT_LINK) - tp->LinkOn = TRUE; - else - tp->LinkOn = FALSE; + return net_dev; +} - tp->LinkChange = FALSE; +static int sis900_mii_probe (struct device * net_dev) +{ + struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv; + int phy_addr; - } - - if (sis900_debug > 1) { - if (tp->full_duplex == FDX_CAPABLE_FULL_SELECTED) { - printk(KERN_INFO "%s: Media type is Full Duplex.\n", - dev->name); - } else { - printk(KERN_INFO "%s: Media type is Half Duplex.\n", - dev->name); - } - if (tp->speeds == HW_SPEED_100_MBPS) { - printk(KERN_INFO "%s: Speed is 100mbps.\n", dev->name); - } else { - printk(KERN_INFO "%s: Speed is 10mbps.\n", dev->name); - } + sis_priv->mii = NULL; + + /* search for total of 32 possible mii phy address */ + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + u16 mii_status; + u16 phy_id0, phy_id1; + int i; + + mii_status = mdio_read(net_dev, phy_addr, MII_STATUS); + if (mii_status == 0xffff || mii_status == 0x0000) + /* the mii is not accessable, try next one */ + continue; + + phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0); + phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1); + + /* search our mii table for the current mii */ + for (i = 0; mii_chip_table[i].phy_id1; i++) + if (phy_id0 == mii_chip_table[i].phy_id0) { + struct mii_phy * mii_phy; + + printk(KERN_INFO + "%s: %s transceiver found at address %d.\n", + net_dev->name, mii_chip_table[i].name, + phy_addr);; + if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) { + mii_phy->chip_info = mii_chip_table+i; + mii_phy->phy_addr = phy_addr; + mii_phy->status = mdio_read(net_dev, phy_addr, + MII_STATUS); + mii_phy->next = sis_priv->mii; + sis_priv->mii = mii_phy; + } + /* the current mii is on our mii_info_table, quit searching (table) */ + break; + } + } + + if (sis_priv->mii == NULL) { + printk(KERN_INFO "%s: No MII transceivers found!\n", net_dev->name); + return 0; } - /* The SiS900-specific entries in the device structure. */ - dev->open = &sis900_open; - dev->hard_start_xmit = &sis900_start_xmit; - dev->stop = &sis900_close; - dev->get_stats = &sis900_get_stats; - dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = &mii_ioctl; - - return dev; -} - -/* Serial EEPROM section. */ - -/* EEPROM_Ctrl bits. */ -#define EECLK 0x00000004 /* EEPROM shift clock. */ -#define EECS 0x00000008 /* EEPROM chip select. */ -#define EEDO 0x00000002 /* EEPROM chip data out. */ -#define EEDI 0x00000001 /* EEPROM chip data in. */ - -/* Delay between EEPROM clock transitions. - No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. - */ + /* FIXME: AMD stuff should be added */ + /* auto negotiate FIXME: not completed */ + elSetCapability(net_dev, sis_priv->mii->phy_addr, 1, 100); + sis_priv->mii->status = elAutoNegotiate(net_dev, sis_priv->mii->phy_addr, + &sis_priv->full_duplex, + &sis_priv->speeds); + + if (sis_priv->mii->status & MIISTAT_LINK) + sis_priv->LinkOn = TRUE; + else + sis_priv->LinkOn = FALSE; + + sis_priv->LinkChange = FALSE; + + return 1; +} +/* Delay between EEPROM clock transitions. */ #define eeprom_delay() inl(ee_addr) -/* The EEPROM commands include the alway-set leading bit. */ -#define EEread 0x0180 -#define EEwrite 0x0140 -#define EEerase 0x01C0 -#define EEwriteEnable 0x0130 -#define EEwriteDisable 0x0100 -#define EEeraseAll 0x0120 -#define EEwriteAll 0x0110 -#define EEaddrMask 0x013F -#define EEcmdShift 16 - +/* Read Serial EEPROM through EEPROM Access Register, Note that location is + in word (16 bits) unit */ static u16 read_eeprom(long ioaddr, int location) { - int i; + int i; u16 retval = 0; long ee_addr = ioaddr + mear; u32 read_cmd = location | EEread; @@ -748,7 +364,7 @@ outl(EECLK, ee_addr); eeprom_delay(); - /* Shift the read command bits out. */ + /* Shift the read command (9) bits out. */ for (i = 8; i >= 0; i--) { u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; outl(dataval, ee_addr); @@ -759,6 +375,7 @@ outb(EECS, ee_addr); eeprom_delay(); + /* read the 16-bits data in */ for (i = 16; i > 0; i--) { outl(EECS, ee_addr); eeprom_delay(); @@ -767,39 +384,21 @@ retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); eeprom_delay(); } - + /* Terminate the EEPROM access. */ outl(0, ee_addr); eeprom_delay(); outl(EECLK, 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. */ + serial MDIO protocol. Note that the command bits and data bits are + send out seperately */ #define mdio_delay() inl(mdio_addr) -#define MIIread 0x6000 -#define MIIwrite 0x6002 -#define MIIpmdMask 0x0F80 -#define MIIpmdShift 7 -#define MIIregMask 0x007C -#define MIIregShift 2 -#define MIIturnaroundBits 2 -#define MIIcmdLen 16 -#define MIIcmdShift 16 -#define MIIreset 0xFFFFFFFF -#define MIIwrLen 32 - -#define MDC 0x00000040 -#define MDDIR 0x00000020 -#define MDIO 0x00000010 - static void mdio_idle(long mdio_addr) { outl(MDIO | MDDIR, mdio_addr); @@ -821,11 +420,11 @@ return; } -static int mdio_read(struct device *dev, int phy_id, int location) +static u16 mdio_read(struct device *dev, int phy_id, int location) { long mdio_addr = dev->base_addr + mear; int mii_cmd = MIIread|(phy_id<= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outl(dataval, mdio_addr); + mdio_delay(); outl(dataval | MDC, mdio_addr); + mdio_delay(); } - /* Read the two transition, 16 data, and wire-idle bits. */ + /* Read the 16 data bits. */ for (i = 16; i > 0; i--) { outl(0, mdio_addr); - //mdio_delay(); + mdio_delay(); retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); outl(MDC, mdio_addr); mdio_delay(); @@ -858,7 +459,7 @@ mdio_idle(mdio_addr); /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { + for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; outb(dataval, mdio_addr); mdio_delay(); @@ -866,6 +467,17 @@ mdio_delay(); } mdio_delay(); + + /* Shift the value bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; + outl(dataval, mdio_addr); + mdio_delay(); + outl(dataval | MDC, mdio_addr); + mdio_delay(); + } + mdio_delay(); + /* Clear out extra bits. */ for (i = 2; i > 0; i--) { outb(0, mdio_addr); @@ -879,110 +491,62 @@ static int sis900_open(struct device *dev) { - struct sis900_private *tp = (struct sis900_private *)dev->priv; + struct sis900_private *sis_priv = (struct sis900_private *)dev->priv; long ioaddr = dev->base_addr; - - if (sis900_debug > 0) - printk(KERN_INFO "%s sis900_open, IO Addr=%x, Irq=%x\n", - dev->name, (unsigned int)ioaddr, dev->irq); + int i = 0; + u32 status = TxRCMP | RxRCMP; /* Soft reset the chip. */ - outl(0, ioaddr + imr); - outl(0, ioaddr + ier); - outl(0, ioaddr + rfcr); - outl(RESET | RxRESET | TxRESET, ioaddr + cr); + sis900_reset(dev); - if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) - { + if (request_irq(dev->irq, &sis900_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_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); - if (tp->tx_bufs == NULL || tp->rx_bufs == NULL) { - if (tp->tx_bufs) - kfree(tp->tx_bufs); - if (tp->rx_bufs) - kfree(tp->rx_bufs); - if (!tp->tx_bufs) { - printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", - dev->name, TX_BUF_SIZE * NUM_TX_DESC); - } - if (!tp->rx_bufs) { - printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", - dev->name, RX_BUF_SIZE * NUM_RX_DESC); - } - return -ENOMEM; - } + if ((sis_priv->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL)) == NULL) { + printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", + dev->name, TX_BUF_SIZE * NUM_TX_DESC); + return -ENOMEM; + } + if ((sis_priv->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL)) == NULL) { + kfree (sis_priv->tx_buf); + printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", + dev->name, RX_BUF_SIZE * NUM_RX_DESC); + return -ENOMEM; + } - { - u32 rfcrSave; - u32 w; - u32 i; - - rfcrSave = inl(rfcr); - outl(rfcrSave & ~RFEN, rfcr); - for (i=0 ; i<3 ; i++) { - w = (u16)*((u16*)(dev->dev_addr)+i); - outl((((u32) i) << RFEP_shift), ioaddr + rfcr); - outl((u32)w, ioaddr + rfdr); - if (sis900_debug > 4) { - printk(KERN_INFO "Filter Addr[%d]=%x\n", - i, inl(ioaddr + rfdr)); - } - } - outl(rfcrSave, rfcr); - } + sis900_init_rxfilter(dev); - sis900_init_ring(dev); - outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); - outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); + sis900_reset_tx_ring(dev); + sis900_reset_rx_ring(dev); if (sis900_debug > 4) - printk(KERN_INFO "txdp:%8.8x\n", inl(ioaddr + txdp)); + printk(KERN_INFO "%s: txdp:%8.8x\n", dev->name, inl(ioaddr + txdp)); - /* Check that the chip has finished the reset. */ - { - u32 status; - int j=0; - status = TxRCMP | RxRCMP; - while (status && (j++ < 30000)) { - status ^= (inl(isr) & status); - } + /* Check that the chip has finished the reset. */ + while (status && (i++ < 30000)) { + status ^= (inl(isr + ioaddr) & status); } outl(PESEL, ioaddr + cfg); - /* Must enable Tx/Rx before setting transfer thresholds! */ - /* - * #define TX_DMA_BURST 0 - * #define RX_DMA_BURST 0 - * #define TX_FIFO_THRESH 16 - * #define TxDRNT_100 (1536>>5) - * #define TxDRNT_10 (1536>>5) - * #define RxDRNT_100 (1536>>5) - * #define RxDRNT_10 (1536>>5) - */ - outl((RX_DMA_BURST<<20) | (RxDRNT_10 << 1), ioaddr+rxcfg); - outl(TxATP | (TX_DMA_BURST << 20) | (TX_FIFO_THRESH<<8) | TxDRNT_10, - ioaddr + txcfg); - if (sis900_debug > 1) - { - if (tp->LinkOn) { - printk(KERN_INFO"%s: Media Type %s%s-duplex.\n", - dev->name, - tp->speeds==HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - tp->full_duplex== FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); - } - else - { - printk(KERN_INFO"%s: Media Link Off\n", dev->name); - } + /* FIXME: should be removed, and replaced by AutoNeogotiate stuff */ + outl((RX_DMA_BURST << RxMXDMA_shift) | (RxDRNT_10 << RxDRNT_shift), + ioaddr + rxcfg); + outl(TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift) | TxDRNT_10, + ioaddr + txcfg); + + if (sis_priv->LinkOn) { + printk(KERN_INFO "%s: Media Type %s%s-duplex.\n", + dev->name, + sis_priv->speeds == HW_SPEED_100_MBPS ? "100mbps " : "10mbps ", + sis_priv->full_duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half"); + } else { + printk(KERN_INFO "%s: Media Link Off\n", dev->name); } + set_rx_mode(dev); dev->tbusy = 0; @@ -990,56 +554,77 @@ dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); - if (sis900_debug > 3) - printk(KERN_INFO "%s: sis900_open() ioaddr %#lx IRQ %d \n", - dev->name, ioaddr, dev->irq); - /* 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 = &sis900_timer; /* timer handler */ - add_timer(&tp->timer); + init_timer(&sis_priv->timer); + sis_priv->timer.expires = jiffies + 2*HZ; + sis_priv->timer.data = (unsigned long)dev; + sis_priv->timer.function = &sis900_timer; + add_timer(&sis_priv->timer); return 0; } +/* set receive filter address to our MAC address */ +static void +sis900_init_rxfilter (struct device * net_dev) +{ + long ioaddr = net_dev->base_addr; + u32 rfcrSave; + u32 i; + + rfcrSave = inl(rfcr + ioaddr); + + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr); + + for (i = 0 ; i < 3 ; i++) { + u32 w; + + w = (u32) *((u16 *)(net_dev->dev_addr)+i); + outl((i << RFADDR_shift), ioaddr + rfcr); + outl(w, ioaddr + rfdr); + + if (sis900_debug > 4) { + printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", + net_dev->name, i, inl(ioaddr + rfdr)); + } + } + outl(rfcrSave, rfcr + ioaddr); +} + static void sis900_timer(unsigned long data) { struct device *dev = (struct device *)data; struct sis900_private *tp = (struct sis900_private *)dev->priv; - int next_tick = 0; + int next_tick = 2*HZ; u16 status; + /* FIXME: call auto negotiate routine to detect link status */ if (!tp->LinkOn) { - status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); if (status & MIISTAT_LINK) { - elPMDreadMode(dev, tp->phys[tp->phy_idx], - &tp->speeds, &tp->full_duplex); + elPMDreadMode(dev, tp->mii->phy_addr, + &tp->speeds, &tp->full_duplex); tp->LinkOn = TRUE; - printk(KERN_INFO "%s: Media Link On %s%s-duplex ", - dev->name, - tp->speeds == HW_SPEED_100_MBPS ? - "100mbps " : "10mbps ", - tp->full_duplex==FDX_CAPABLE_FULL_SELECTED ? - "full" : "half"); + printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", dev->name, + tp->speeds == HW_SPEED_100_MBPS ? "100mbps " : "10mbps ", + tp->full_duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half"); } } else { // previous link on - status = mdio_read(dev, tp->phys[tp->phy_idx], MII_STATUS); + status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); if (!(status & MIISTAT_LINK)) { tp->LinkOn = FALSE; printk(KERN_INFO "%s: Media Link Off\n", dev->name); } } - next_tick = 2*HZ; if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); + tp->timer.expires = jiffies + next_tick; add_timer(&tp->timer); } } @@ -1068,20 +653,9 @@ " (queue head)" : ""); } - /* Soft reset the chip. */ - //outb(RESET, ioaddr + cr); - /* Check that the chip has finished the reset. */ - /* - for (i = 1000; i > 0; i--) - if ((inb(ioaddr + cr) & RESET) == 0) - break; - */ tp->cur_rx = 0; - /* Must enable Tx/Rx before setting transfer thresholds! */ - /* - set_rx_mode(dev); - */ + { /* Save the unsent Tx packets. */ struct sk_buff *saved_skb[NUM_TX_DESC], *skb; int j; @@ -1095,11 +669,6 @@ memcpy((unsigned char*)(tp->tx_buf[i].buf), skb->data, skb->len); tp->tx_buf[i].cmdsts = OWN | skb->len; - /* Note: the chip doesn't have auto-pad! */ - /* - outl(tp->tx_flag|(skb->len>=ETH_ZLEN?skb->len:ETH_ZLEN), - ioaddr + TxStatus0 + i*4); - */ } outl(TxENA, ioaddr + cr); tp->cur_tx = i; @@ -1116,28 +685,27 @@ dev->trans_start = jiffies; tp->stats.tx_errors++; /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); return; } - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +/* Reset (Initialize) the Tx rings, along with various 'dev' bits. */ static void -sis900_init_ring(struct device *dev) +sis900_reset_tx_ring(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; int i; - + tp->tx_full = 0; - tp->cur_rx = 0; tp->dirty_tx = tp->cur_tx = 0; - + /* Tx Buffer */ for (i = 0; i < NUM_TX_DESC; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; + tp->tx_skbuff[i] = 0; + tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; tp->tx_buf[i].bufPhys = - virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); + virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); } /* Tx Descriptor */ @@ -1152,6 +720,19 @@ tp->tx_buf[i].cmdsts=0; } + outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); +} + +/* Reset (Initialize) the Rx rings, along with various 'dev' bits. */ +static void +sis900_reset_rx_ring(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + tp->cur_rx = 0; + /* Rx Buffer */ for (i = 0; i < NUM_RX_DESC; i++) { tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; @@ -1170,6 +751,8 @@ virt_to_bus(&(tp->rx_buf[i].plink)); tp->rx_buf[i].cmdsts=RX_BUF_SIZE; } + + outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); } static int @@ -1241,7 +824,8 @@ #if defined(__i386__) /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ - if (test_and_set_bit(0, (void*)&dev->interrupt)) { + if (test_and_set_bit(0, (void*)&dev->interrupt) +) { printk(KERN_INFO "%s: SMP simultaneous entry of " "an interrupt handler.\n", dev->name); dev->interrupt = 0; /* Avoid halting machine. */ @@ -1258,8 +842,6 @@ do { status = inl(ioaddr + isr); - /* Acknowledge all of the current interrupt sources ASAP. */ - outl(status, ioaddr + isr); // ????? if (sis900_debug > 4) printk(KERN_INFO "%s: interrupt status=%#4.4x " @@ -1267,6 +849,7 @@ dev->name, status, inl(ioaddr + isr)); if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + /* nothing intresting happened */ break; } @@ -1354,7 +937,7 @@ /* Free the original skb. */ if (sis900_debug > 2) printk(KERN_INFO "Free original skb\n"); - dev_free_skb(tp->tx_skbuff[entry]); + dev_kfree_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } // for dirty @@ -1419,8 +1002,6 @@ return; } -/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the - field alignments and semantics. */ static int sis900_rx(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; @@ -1533,6 +1114,7 @@ dev->name, cur_rx, inb(ioaddr + cr)); tp->cur_rx = cur_rx; + outl( RxENA , ioaddr + cr ); /* LCS */ return 0; } @@ -1546,10 +1128,6 @@ dev->start = 0; dev->tbusy = 1; - if (sis900_debug > 1) - printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", - dev->name, inl(ioaddr + isr)); - /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); @@ -1562,7 +1140,7 @@ for (i = 0; i < NUM_TX_DESC; i++) { if (tp->tx_skbuff[i]) - dev_free_skb(tp->tx_skbuff[i]); + dev_kfree_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } kfree(tp->rx_bufs); @@ -1581,13 +1159,13 @@ u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { - case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - data[0] = tp->phys[tp->phy_idx]; + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = tp->mii->phy_addr; /* Fall Through */ - case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); return 0; - case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!suser()) return -EPERM; mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); @@ -1727,17 +1305,17 @@ retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO, FALSE,&status); if (!retnVal) { - printk(KERN_INFO "Not wait for Reset Complete\n"); + printk(KERN_INFO "%s: Not wait for Reset Complete\n", dev->name); } retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE, TRUE, &status); if (!retnVal) { - printk(KERN_INFO "Not wait for AutoNego Complete\n"); + printk(KERN_INFO "%s: Not wait for AutoNego Complete\n", dev->name); } retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK, TRUE, &status); if (!retnVal) { - printk(KERN_INFO "Not wait for Link Complete\n"); + printk(KERN_INFO "%s: Not wait for Link Complete\n", dev->name); } if (status & MIISTAT_LINK) { elPMDreadMode(dev, phy_id, speed, duplex); @@ -1814,9 +1392,8 @@ for (i=0 ; i<8 ; i++) mc_filter[i]=0xffff; } else if ((dev->mc_count > multicast_filter_limit) - || (dev->flags & IFF_ALLMULTI)) { - rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | - ACCEPT_CAM_QUALIFIED; + || (dev->flags & IFF_ALLMULTI)) { + rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | ACCEPT_CAM_QUALIFIED; for (i=0 ; i<8 ; i++) mc_filter[i]=0xffff; } else { @@ -1875,49 +1452,40 @@ static void sis900_reset(struct device *dev) { long ioaddr = dev->base_addr; - + outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET, ioaddr + cr); outl(PESEL, ioaddr + cfg); - + set_rx_mode(dev); } #ifdef MODULE int init_module(void) { - return sis900_probe(0); + return sis900_probe(NULL); } void cleanup_module(void) { - struct device *next_dev; + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_sis900_dev) { + struct sis900_private *tp = + (struct sis900_private *)root_sis900_dev->priv; + struct device *next_dev = tp->next_module; + + unregister_netdev(root_sis900_dev); + release_region(root_sis900_dev->base_addr, + tp->mac->io_size); + kfree(tp); + kfree(root_sis900_dev); - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ - while (root_sis900_dev) { - struct sis900_private *tp = - (struct sis900_private *)root_sis900_dev->priv; - next_dev = tp->next_module; - unregister_netdev(root_sis900_dev); - release_region(root_sis900_dev->base_addr, - pci_tbl[tp->chip_id].io_size); - kfree(tp); - kfree(root_sis900_dev); - root_sis900_dev = next_dev; + root_sis900_dev = next_dev; } } #endif /* MODULE */ -/* - * Local variables: - * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c sis900.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/sis900.h linux.14p2/drivers/net/sis900.h --- linux.vanilla/drivers/net/sis900.h Thu Jan 1 01:00:00 1970 +++ linux.14p2/drivers/net/sis900.h Sat Oct 23 22:40:29 1999 @@ -0,0 +1,256 @@ +/* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 + * Copyrigth 1999 Silicon Integrated System Corporation + * References: + * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + * preliminary Rev. 1.0 Jan. 14, 1998 + * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + * preliminary Rev. 1.0 Nov. 10, 1998 + * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + * preliminary Rev. 1.0 Jan. 18, 1998 + * http://www.sis.com.tw/support/databook.htm + */ + +/* MAC operationl registers of SiS 7016 and SiS 900 ehternet controller */ +/* The I/O extent, SiS 900 needs 256 bytes of io address */ +#define SIS900_TOTAL_SIZE 0x100 + +/* Symbolic offsets to registers. */ +enum SIS900_registers { + cr=0x0, //Command Register + cfg=0x4, //Configuration Register + mear=0x8, //EEPROM Access Register + ptscr=0xc, //PCI Test Control Register + isr=0x10, //Interrupt Status Register + imr=0x14, //Interrupt Mask Register + ier=0x18, //Interrupt Enable Register + epar=0x18, //Enhanced PHY Access Register + txdp=0x20, //Transmit Descriptor Pointer Register + txcfg=0x24, //Transmit Configuration Register + rxdp=0x30, //Receive Descriptor Pointer Register + rxcfg=0x34, //Receive Configuration Register + flctrl=0x38, //Flow Control Register + rxlen=0x3c, //Receive Packet Length Register + rfcr=0x48, //Receive Filter Control Register + rfdr=0x4C, //Receive Filter Data Register + pmctrl=0xB0, //Power Management Control Register + pmer=0xB4 //Power Management Wake-up Event Register +}; + +/* Symbolic names for bits in various registers */ +enum sis900_command_register_bits { + RESET = 0x00000100, SWI = 0x00000080, RxRESET = 0x00000020, + TxRESET = 0x00000010, RxDIS = 0x00000008, RxENA = 0x00000004, + TxDIS = 0x00000002, TxENA = 0x00000001 +}; + +enum sis900_configuration_register_bits { + DESCRFMT = 0x00000100 /* 7016 specific */, REQALG = 0x00000080, + SB = 0x00000040, POW = 0x00000020, EXD = 0x00000010, + PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001 +}; + +enum sis900_eeprom_access_reigster_bits { + MDC = 0x00000040, MDDIR = 0x00000020, MDIO = 0x00000010, /* 7016 specific */ + EECS = 0x00000008, EECLK = 0x00000004, EEDO = 0x00000002, + EEDI = 0x00000001 +}; + +enum sis900_interrupt_register_bits { + WKEVT = 0x10000000, TxPAUSEEND = 0x08000000, TxPAUSE = 0x04000000, + TxRCMP = 0x02000000, RxRCMP = 0x01000000, DPERR = 0x00800000, + SSERR = 0x00400000, RMABT = 0x00200000, RTABT = 0x00100000, + RxSOVR = 0x00010000, HIBERR = 0x00008000, SWINT = 0x00001000, + MIBINT = 0x00000800, TxURN = 0x00000400, TxIDLE = 0x00000200, + TxERR = 0x00000100, TxDESC = 0x00000080, TxOK = 0x00000040, + RxORN = 0x00000020, RxIDLE = 0x00000010, RxEARLY = 0x00000008, + RxERR = 0x00000004, RxDESC = 0x00000002, RxOK = 0x00000001 +}; + +enum sis900_interrupt_enable_reigster_bits { + IE = 0x00000001 +}; + +/* maximum dma burst fro transmission and receive*/ +#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */ +#define TxMXDMA_shift 20 +#define RxMXDMA_shift 20 +#define TX_DMA_BURST 0 +#define RX_DMA_BURST 0 + +/* transmit FIFO threshholds */ +#define TX_FILL_THRESH 16 +#define TxFILLT_shift 8 +#define TxDRNT_shift 0 +#define TxDRNT_100 (1536>>5) +#define TxDRNT_10 16 + +enum sis900_transmit_config_register_bits { + TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000, + TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00, + TxDRNT = 0x0000003F +}; + +/* recevie FFIFO thresholds */ +#define RxDRNT_shift 1 +#define RxDRNT_100 8 +#define RxDRNT_10 8 + +enum sis900_reveive_config_register_bits { + RxAEP = 0x80000000, RxARP = 0x40000000, RxATP = 0x10000000, + RxAJAB = 0x08000000, RxDRNT = 0x0000007F +}; + +#define RFAA_shift 28 +#define RFADDR_shift 16 + +enum sis900_receive_filter_control_register_bits { + RFEN = 0x80000000, RFAAB = 0x40000000, RFAAM = 0x20000000, + RFAAP = 0x10000000, RFPromiscuous = (RFAAB|RFAAM|RFAAP) +}; + +enum sis900_reveive_filter_data_mask { + RFDAT = 0x0000FFFF +}; + +/* EEPROM Addresses */ +enum sis900_eeprom_address { + EEPROMSignature = 0x00, EEPROMVendorID = 0x02, EEPROMDeviceID = 0x03, + EEPROMMACAddr = 0x08, EEPROMChecksum = 0x0b +}; + +/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ +enum sis900_eeprom_command { + EEread = 0x0180, EEwrite = 0x0140, EEerase = 0x01C0, + EEwriteEnable = 0x0130, EEwriteDisable = 0x0100, + EEeraseAll = 0x0120, EEwriteAll = 0x0110, + EEaddrMask = 0x013F, EEcmdShift = 16 +}; + +/* Manamgement Data I/O (mdio) frame */ +#define MIIread 0x6000 +#define MIIwrite 0x5002 +#define MIIpmdShift 7 +#define MIIregShift 2 +#define MIIcmdLen 16 +#define MIIcmdShift 16 + +/* Buffer Descriptor */ +#define OWN 0x80000000 +#define MORE 0x40000000 +#define INTR 0x20000000 +#define OK 0x08000000 +#define DSIZE 0x00000FFF + +#define SUPCRC 0x10000000 +#define ABORT 0x04000000 +#define UNDERRUN 0x02000000 +#define NOCARRIER 0x01000000 +#define DEFERD 0x00800000 +#define EXCDEFER 0x00400000 +#define OWCOLL 0x00200000 +#define EXCCOLL 0x00100000 +#define COLCNT 0x000F0000 + +#define INCCRC 0x10000000 +// ABORT 0x04000000 +#define OVERRUN 0x02000000 +#define DEST 0x01800000 +#define BCAST 0x01800000 +#define MCAST 0x01000000 +#define UNIMATCH 0x00800000 +#define TOOLONG 0x00400000 +#define RUNT 0x00200000 +#define RXISERR 0x00100000 +#define CRCERR 0x00080000 +#define FAERR 0x00040000 +#define LOOPBK 0x00020000 +#define RXCOL 0x00010000 + +#define RXSTS_shift 18 + +/* MII register offsets */ +#define MII_CONTROL 0x0000 +#define MII_STATUS 0x0001 +#define MII_PHY_ID0 0x0002 +#define MII_PHY_ID1 0x0003 +#define MII_ANAR 0x0004 +#define MII_ANLPAR 0x0005 +#define MII_ANER 0x0006 +/* MII Control register bit definitions. */ +#define MIICNTL_FDX 0x0100 +#define MIICNTL_RST_AUTO 0x0200 +#define MIICNTL_ISOLATE 0x0400 +#define MIICNTL_PWRDWN 0x0800 +#define MIICNTL_AUTO 0x1000 +#define MIICNTL_SPEED 0x2000 +#define MIICNTL_LPBK 0x4000 +#define MIICNTL_RESET 0x8000 +/* MII Status register bit significance. */ +#define MIISTAT_EXT 0x0001 +#define MIISTAT_JAB 0x0002 +#define MIISTAT_LINK 0x0004 +#define MIISTAT_CAN_AUTO 0x0008 +#define MIISTAT_FAULT 0x0010 +#define MIISTAT_AUTO_DONE 0x0020 +#define MIISTAT_CAN_T 0x0800 +#define MIISTAT_CAN_T_FDX 0x1000 +#define MIISTAT_CAN_TX 0x2000 +#define MIISTAT_CAN_TX_FDX 0x4000 +#define MIISTAT_CAN_T4 0x8000 +/* MII NWAY Register Bits ... +** valid for the ANAR (Auto-Negotiation Advertisement) and +** ANLPAR (Auto-Negotiation Link Partner) registers */ +#define MII_NWAY_NODE_SEL 0x001f +#define MII_NWAY_CSMA_CD 0x0001 +#define MII_NWAY_T 0x0020 +#define MII_NWAY_T_FDX 0x0040 +#define MII_NWAY_TX 0x0080 +#define MII_NWAY_TX_FDX 0x0100 +#define MII_NWAY_T4 0x0200 +#define MII_NWAY_RF 0x2000 +#define MII_NWAY_ACK 0x4000 +#define MII_NWAY_NP 0x8000 + +/* MII Auto-Negotiation Expansion Register Bits */ +#define MII_ANER_PDF 0x0010 +#define MII_ANER_LP_NP_ABLE 0x0008 +#define MII_ANER_NP_ABLE 0x0004 +#define MII_ANER_RX_PAGE 0x0002 +#define MII_ANER_LP_AN_ABLE 0x0001 +#define HALF_DUPLEX 1 +#define FDX_CAPABLE_DUPLEX_UNKNOWN 2 +#define FDX_CAPABLE_HALF_SELECTED 3 +#define FDX_CAPABLE_FULL_SELECTED 4 +#define HW_SPEED_UNCONFIG 0 +#define HW_SPEED_10_MBPS 10 +#define HW_SPEED_100_MBPS 100 +#define HW_SPEED_DEFAULT (HW_SPEED_10_MBPS) + +#define ACCEPT_ALL_PHYS 0x01 +#define ACCEPT_ALL_MCASTS 0x02 +#define ACCEPT_ALL_BCASTS 0x04 +#define ACCEPT_ALL_ERRORS 0x08 +#define ACCEPT_CAM_QUALIFIED 0x10 +#define MAC_LOOPBACK 0x20 + +//#define FDX_CAPABLE_FULL_SELECTED 4 +#define CRC_SIZE 4 +#define MAC_HEADER_SIZE 14 + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ +#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */ + +#define TRUE 1 +#define FALSE 0 + +/* PCI stuff, should be move to pci.h */ +#define PCI_DEVICE_ID_SI_900 0x900 +#define PCI_DEVICE_ID_SI_7016 0x7016 + +/* ioctl for accessing MII transveiver */ +#define SIOCGMIIPHY (SIOCDEVPRIVATE) /* Get the PHY in use. */ +#define SIOCGMIIREG (SIOCDEVPRIVATE+1) /* Read a PHY register. */ +#define SIOCSMIIREG (SIOCDEVPRIVATE+2) /* Write a PHY register */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/net/tulip.c linux.14p2/drivers/net/tulip.c --- linux.vanilla/drivers/net/tulip.c Sat Aug 14 02:27:30 1999 +++ linux.14p2/drivers/net/tulip.c Wed Oct 27 20:23:29 1999 @@ -1,13 +1,13 @@ /* tulip.c: A DEC 21040-family ethernet driver for Linux. */ /* - Written 1994-1998 by Donald Becker. + Written/copyright 1994-1999 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 Digital "Tulip" ethernet adapter interface. + This driver is for the Digital "Tulip" Ethernet adapter interface. It should work with most DEC 21*4*-based chips/ethercards, as well as - PNIC and MXIC chips. + with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences @@ -18,7 +18,7 @@ */ #define SMP_CHECK -static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; +static const char version[] = "tulip.c:v0.91g 7/16/99 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ @@ -60,80 +60,134 @@ static int rx_copybreak = 100; #endif +/* + Set the bus performance register. + Typical: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 No alignment 0x00000000 unlimited 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Warning: many older 486 systems are broken and require setting 0x00A04800 + 8 longword cache alignment, 8 longword burst. + ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__powerpc__) +static int csr0 = 0x01B00000 | 0x8000; +#elif defined(__sparc__) +static int csr0 = 0x01B00080 | 0x8000; +#elif defined(__i386__) +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (4*HZ) +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 +#if !defined(__OPTIMIZE__) || !defined(__KERNEL__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include +#include +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif #include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + #include #include #include #include -#include #include #include #include #include #include - -#include /* Processor type for cache alignment. */ -#include -#include -#include - #include #include #include +#include /* Processor type for cache alignment. */ +#include +#include +#include -/* Kernel compatibility defines, common to David Hind's PCMCIA package. +/* Kernel compatibility defines, some common to David Hind's PCMCIA package. This is only in the support-all-kernels source code. */ -#include /* Evil, but neccessary */ -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 -#define RUN_AT(x) (x) /* What to put in timer->expires. */ -#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) -#define virt_to_bus(addr) ((unsigned long)addr) -#define bus_to_virt(addr) ((void*)addr) +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Donald Becker "); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif -#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 >= 0x10344) -#define NEW_MULTICAST -#include -#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) +#if (LINUX_VERSION_CODE >= 0x20100) +static char kernel_version[] = UTS_RELEASE; #endif -#if (LINUX_VERSION_CODE < 0x20123) +#if LINUX_VERSION_CODE < 0x20123 #define hard_smp_processor_id() smp_processor_id() #define test_and_set_bit(val, addr) set_bit(val, addr) +#define le16_to_cpu(val) (val) +#define le32_to_cpu(val) (val) +#define cpu_to_le32(val) (val) #endif - -/* This my implementation of shared IRQs, now only used for 1.2.13. */ -#ifdef HAVE_SHARED_IRQ -#define USE_SHARED_IRQ -#include +#if LINUX_VERSION_CODE <= 0x20139 +#define net_device_stats enet_statistics +#else +#define NETSTATS_VER2 #endif - -/* The total size is unusually large: The 21040 aligns each of its 16 - longword-wide registers on a quadword boundary. */ -#define TULIP_TOTAL_SIZE 0x80 - -#ifdef HAVE_DEVLIST -struct netdev_entry tulip_drv = -{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL}; +#if LINUX_VERSION_CODE < 0x20155 +/* Grrrr, the PCI code changed, but did not consider CardBus... */ +#include +#define PCI_SUPPORT_VER1 +#else +#define PCI_SUPPORT_VER2 +#endif +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif +#if ! defined(CAP_NET_ADMIN) +#define capable(CAP_XXX) (suser()) +#endif +#if ! defined(HAS_NETIF_QUEUE) +#define netif_wake_queue(dev) mark_bh(NET_BH); #endif +#define tulip_debug debug #ifdef TULIP_DEBUG -int tulip_debug = TULIP_DEBUG; +static int tulip_debug = TULIP_DEBUG; #else -int tulip_debug = 1; +static int tulip_debug = 1; #endif /* @@ -143,17 +197,27 @@ This device driver is designed for the DECchip "Tulip", Digital's single-chip ethernet controllers for PCI. Supported members of the family -are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on -many PCI boards including the SMC EtherPower series. - +are the 21040, 21041, 21140, 21140A, 21142, and 21143. Similar work-alike +chips from Lite-On, Macronics, ASIX, Compex and other listed below are also +supported. + +These chips are used on at least 140 unique PCI board designs. The great +number of chips and board designs supported is the reason for the +driver size and complexity. Almost of the increasing complexity is in the +board configuration and media selection code. There is very little +increasing in the operational critical path length. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The system BIOS preferably should assign the PCI INTA signal to an otherwise unused system IRQ line. -Note: Kernel versions earlier than 1.3.73 do not support shared PCI -interrupt lines. + +Some boards have EEPROMs tables with default media entry. The factory default +is usually "autoselect". This should only be overridden when using +transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) +for forcing full-duplex when used with old link partners that do not do +autonegotiation. III. Driver operation @@ -201,111 +265,159 @@ IV. Notes -Thanks to Duke Kamstra of SMC for providing an EtherPower board. +Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. +Greg LaPolla at Linksys provided PNIC and other Linksys boards. +Znyx provided a four-port card for testing. IVb. References http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html http://www.digital.com (search for current 21*4* datasheets and "21X4 SROM") -http://www.national.com/pf/DP/DP83840.html +http://www.national.com/pf/DP/DP83840A.html +http://www.asix.com.tw/pmac.htm +http://www.admtek.com.tw/ IVc. Errata -The DEC databook doesn't document which Rx filter settings accept broadcast -packets. Nor does it document how to configure the part to configure the -serial subsystem for normal (vs. loopback) operation or how to have it -autoswitch between internal 10baseT, SIA and AUI transceivers. - +The old DEC databooks were light on details. The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last -register of the set CSR12-15 written. Hmmm, now how is that possible? */ - - -/* A few values that may be tweaked. */ -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +register of the set CSR12-15 written. Hmmm, now how is that possible? -/* This is a mysterious value that can be written to CSR11 in the 21040 (only) - to support a pre-NWay full-duplex signaling mechanism using short frames. - No one knows what it should be, but if left at its default value some - 10base2(!) packets trigger a full-duplex-request interrupt. */ -#define FULL_DUPLEX_MAGIC 0x6969 +The DEC SROM format is very badly designed not precisely defined, leading to +part of the media selection junkheap below. Some boards do not have EEPROM +media tables and need to be patched up. Worse, other boards use the DEC +design kit media table when it isn't correct for their board. -#ifndef PCI_VENDOR_ID_DEC /* Now defined in linux/pci.h */ -#define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_DEVICE_ID_TULIP 0x0002 /* 21040. */ -#define PCI_DEVICE_ID_TULIP_FAST 0x0009 /* 21140. */ -#endif +We cannot use MII interrupts because there is no defined GPIO pin to attach +them. The MII transceiver status is polled using an kernel timer. -#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS -#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */ -#endif -#ifndef PCI_DEVICE_ID_DEC_TULIP_21142 -#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019 -#endif +*/ -#ifndef PCI_VENDOR_ID_LITEON -#define PCI_VENDOR_ID_LITEON 0x11AD -#endif +static struct device * +tulip_probe1(int pci_bus, int pci_devfn, struct device *dev, long ioaddr, + int irq, int chip_idx, int board_idx); + +/* This table drives the PCI probe routines. It's mostly boilerplate in all + of the drivers, and will likely be provided by some future kernel. + Note the matching code -- the first table entry matchs all 56** cards but + second only the 1234 card. +*/ +enum pci_flags_bit { + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; +#define PCI_ADDR0_IO (PCI_USES_IO|PCI_ADDR0) -#ifndef PCI_VENDOR_ID_MXIC -#define PCI_VENDOR_ID_MXIC 0x10d9 -#define PCI_DEVICE_ID_MX98713 0x0512 -#define PCI_DEVICE_ID_MX98715 0x0531 -#define PCI_DEVICE_ID_MX98725 0x0531 -#endif +struct pci_id_info { + const char *name; + u16 vendor_id, device_id, device_id_mask, flags; + int io_size, min_latency; + struct device *(*probe1)(int pci_bus, int pci_devfn, struct device *dev, + long ioaddr, int irq, int chip_idx, int fnd_cnt); +}; +#ifndef CARDBUS +static struct pci_id_info pci_tbl[] = { + { "Digital DC21040 Tulip", + 0x1011, 0x0002, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DC21041 Tulip", + 0x1011, 0x0014, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21140 Tulip", + 0x1011, 0x0009, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Digital DS21143 Tulip", + 0x1011, 0x0019, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Lite-On 82c168 PNIC", + 0x11AD, 0x0002, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98713 PMAC", + 0x10d9, 0x0512, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98715 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Macronix 98725 PMAC", + 0x10d9, 0x0531, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "ASIX AX88140", + 0x125B, 0x1400, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Lite-On LC82C115 PNIC-II", + 0x11AD, 0xc115, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "ADMtek AN981 Comet", + 0x1317, 0x0981, 0xffff, PCI_ADDR0_IO, 256, 32, tulip_probe1 }, + { "Compex RL100-TX", + 0x11F6, 0x9881, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Intel 21145 Tulip", + 0x8086, 0x0039, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + { "Xircom Tulip clone", + 0x115d, 0x0003, 0xffff, PCI_ADDR0_IO, 128, 32, tulip_probe1 }, + {0}, +}; +#endif /* !CARD_BUS */ -/* The rest of these values should never change. */ +/* This table use during operation for capabilities and media timer. */ static void tulip_timer(unsigned long data); static void t21142_timer(unsigned long data); static void mxic_timer(unsigned long data); static void pnic_timer(unsigned long data); +static void comet_timer(unsigned long data); -/* A table describing the chip types. */ -enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; +enum tbl_flag { + HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, + HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ + HAS_PNICNWAY=0x80, HAS_NWAY143=0x40, /* Uses internal NWay xcvr. */ + HAS_8023X=0x100, +}; static struct tulip_chip_table { - int vendor_id, device_id; char *chip_name; int io_size; int valid_intrs; /* CSR7 interrupt enable settings */ int flags; void (*media_timer)(unsigned long data); } tulip_tbl[] = { - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, - "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, - "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, - "Digital DS21140 Tulip", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, - tulip_timer }, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, - "Digital DS21142/3 Tulip", 128, 0x0801fbff, - HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, - { PCI_VENDOR_ID_LITEON, 0x0002, - "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, - "Macronix 98713 PMAC", 128, 0x0001ebef, - HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, - "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, - "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, - { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { "Digital DC21041 Tulip", 128, 0x0001ebff, HAS_MEDIA_TABLE, tulip_timer }, + { "Digital DS21140 Tulip", 128, 0x0001ebef, HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, - {0, 0, 0, 0}, + { "Digital DS21143 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + { "Lite-On 82c168 PNIC", 256, 0x0001ebef, + HAS_MII | HAS_PNICNWAY, pnic_timer }, + { "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Macronix 98715 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "Macronix 98725 PMAC", 256, 0x0001ebef, + HAS_MEDIA_TABLE, mxic_timer }, + { "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY, tulip_timer }, + { "Lite-On PNIC-II", 256, 0x0801fbff, + HAS_MII | HAS_NWAY143 | HAS_8023X, t21142_timer }, + { "ADMtek Comet", 256, 0x0001abef, + MC_HASH_ONLY, comet_timer }, + { "Compex 9881 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, + { "Intel DS21145 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + { "Xircom tulip work-alike", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143, + t21142_timer }, + {0}, +}; +/* This matches the table above. Note 21142 == 21143. */ +enum chips { + DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725, AX88140, PNIC2, COMET, COMPEX9881, + I21145, }; -/* This matches the table above. */ -enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, - LC82C168, MX98713, MX98715, MX98725}; /* A full-duplex map for media types. */ -enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, - MediaIs100=16}; +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 }; +static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0}; /* 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_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; @@ -327,10 +439,6 @@ TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, }; -enum desc_status_bits { - DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, -}; - /* The Tulip Rx and Tx buffer descriptors. */ struct tulip_rx_desc { s32 status; @@ -344,6 +452,22 @@ u32 buffer1, buffer2; /* We use only buffer 1. */ }; +enum desc_status_bits { + DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicates ring mode, but always write the 'next' field for + chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + +#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ + struct medialeaf { u8 type; u8 media; @@ -353,7 +477,8 @@ struct mediatable { u16 defaultmedia; u8 leafcount, csr12dir; /* General purpose pin directions. */ - unsigned has_mii:1, has_nonmii:1; + unsigned has_mii:1, has_nonmii:1, has_reset:6; + u32 csr15dir, csr15val; /* 21143 NWay setting. */ struct medialeaf mleaf[0]; }; @@ -375,19 +500,13 @@ /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; char *rx_buffs; /* Address of temporary Rx buffers. */ - u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */ + u16 setup_frame[96]; /* Pseudo-Tx frame to init address table. */ int chip_id; int revision; -#if LINUX_VERSION_CODE > 0x20139 + int flags; struct net_device_stats stats; -#else - struct enet_statistics stats; -#endif struct timer_list timer; /* Media selection timer. */ int interrupt; /* In-interrupt flag. */ -#ifdef SMP_CHECK - 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. */ @@ -398,134 +517,130 @@ 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 nway:1, nwayset:1; /* 21143 internal NWay. */ + unsigned int csr0; /* CSR0 setting. */ unsigned int csr6; /* Current CSR6 control settings. */ - unsigned char eeprom[128]; /* Serial EEPROM contents. */ + unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ + void (*link_change)(struct device *dev, int csr5); u16 to_advertise; /* NWay capabilities advertised. */ + u16 lpar; /* 21143 Link partner ability. */ 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_dev_fn; + int saved_if_port; + unsigned char pci_bus, pci_devfn; int pad0, pad1; /* Used for 8-byte alignment */ }; -static struct device *tulip_probe1(int pci_bus, int pci_devfn, - struct device *dev, - int chip_id, int options); static void parse_eeprom(struct device *dev); -static int read_eeprom(long ioaddr, int location); +static int read_eeprom(long ioaddr, int location, int addr_len); static int mdio_read(struct device *dev, int phy_id, int location); static void mdio_write(struct device *dev, int phy_id, int location, int value); static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); -static void tulip_timer(unsigned long data); +/* Chip-specific media selection (timer functions prototyped above). */ +static void t21142_lnk_change(struct device *dev, int csr5); +static void t21142_start_nway(struct device *dev); +static void pnic_lnk_change(struct device *dev, int csr5); +static void pnic_do_nway(struct device *dev); + static void tulip_tx_timeout(struct device *dev); static void tulip_init_ring(struct device *dev); static int tulip_start_xmit(struct sk_buff *skb, struct device *dev); static int tulip_rx(struct device *dev); -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); -static struct enet_statistics *tulip_get_stats(struct device *dev); +static struct net_device_stats *tulip_get_stats(struct device *dev); #ifdef HAVE_PRIVATE_IOCTL static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); #endif -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev); -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); -#endif -/* A list of all installed Tulip devices, for removing the driver module. */ +/* A list of all installed Tulip devices. */ static struct device *root_tulip_dev = NULL; -/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, - but now receives directly into full-sized skbuffs that are allocated - at open() time. - This allows the probe routine to use the old driver initialization - interface. */ - +#ifndef CARDBUS int tulip_probe(struct device *dev) { int cards_found = 0; - static int pci_index = 0; /* Static, for multiple probe calls. */ + int pci_index = 0; unsigned char pci_bus, pci_device_fn; - /* Ideally we would detect all network cards in slot order. That would - be best done a central PCI probe dispatch, which wouldn't work - well with the current structure. So instead we detect just the - Tulip cards in slot order. */ - -#if LINUX_VERSION_CODE >= 0x20155 - if (! pci_present()) - return -ENODEV; -#else - if (! pcibios_present()) + if ( ! pcibios_present()) return -ENODEV; -#endif + for (;pci_index < 0xff; pci_index++) { u16 vendor, device, pci_command, new_command; - unsigned long pci_ioaddr = 0; - int chip_idx = 0; + int chip_idx; + int irq; + long ioaddr; if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, reverse_probe ? 0xfe - pci_index : pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) { if (reverse_probe) continue; else break; + } pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); - for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) - if (vendor == tulip_tbl[chip_idx].vendor_id && - device == tulip_tbl[chip_idx].device_id) + for (chip_idx = 0; pci_tbl[chip_idx].vendor_id; chip_idx++) + if (vendor == pci_tbl[chip_idx].vendor_id + && (device & pci_tbl[chip_idx].device_id_mask) == + pci_tbl[chip_idx].device_id) break; - if (tulip_tbl[chip_idx].chip_name == 0) { - if (vendor == PCI_VENDOR_ID_DEC || - vendor == PCI_VENDOR_ID_LITEON) - printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" - " %4.4x %4.4x"" detected: not configured.\n", - vendor, device); + if (pci_tbl[chip_idx].vendor_id == 0) continue; - } -#if LINUX_VERSION_CODE >= 0x20155 - pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); + + { +#if defined(PCI_SUPPORT_VER2) + struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn); + ioaddr = pdev->base_address[0] & ~3; + irq = pdev->irq; +#else + u32 pci_ioaddr; + u8 pci_irq_line; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + ioaddr = pci_ioaddr & ~3; + irq = pci_irq_line; #endif - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; + } - if (tulip_debug > 2) - printk(KERN_DEBUG "Found %s at I/O %#lx.\n", - tulip_tbl[chip_idx].chip_name, pci_ioaddr); + if (debug > 2) + printk(KERN_INFO "Found %s at PCI I/O address %#lx.\n", + pci_tbl[chip_idx].name, ioaddr); - if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) continue; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; if (pci_command != new_command) { - printk(KERN_INFO " The PCI BIOS has not enabled this" - " device! Updating PCI command %4.4x->%4.4x.\n", - pci_command, new_command); + printk(KERN_INFO " The PCI BIOS has not enabled the" + " device at %d/%d! Updating PCI command %4.4x->%4.4x.\n", + pci_bus, pci_device_fn, pci_command, new_command); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); } - dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); + dev = pci_tbl[chip_idx].probe1(pci_bus, pci_device_fn, dev, ioaddr, + irq, chip_idx, cards_found); /* Get and check the bus-master and latency values. */ if (dev) { - unsigned char pci_latency; + u8 pci_latency; pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 10) { @@ -534,75 +649,62 @@ pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 64); - } else if (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " - " PCI command is %4.4x.\n", - pci_latency, new_command); - /* Bring the 21143 out power-down mode. */ - if (device == PCI_DEVICE_ID_DEC_TULIP_21142) - pcibios_write_config_dword(pci_bus, pci_device_fn, - 0x40, 0x40000000); - dev = 0; - cards_found++; + } } + dev = 0; + cards_found++; } return cards_found ? 0 : -ENODEV; } +#endif /* not CARDBUS */ -static struct device *tulip_probe1(int pci_bus, int pci_device_fn, - struct device *dev, - int chip_id, int board_idx) +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, long ioaddr, int irq, + int chip_idx, int board_idx) { static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; - long ioaddr; - int irq; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ + u8 chip_rev; int i; unsigned short sum; + u8 ee_data[EEPROM_SIZE]; if (tulip_debug > 0 && did_version++ == 0) printk(KERN_INFO "%s", version); dev = init_etherdev(dev, 0); -#if LINUX_VERSION_CODE >= 0x20155 - irq = pci_find_slot(pci_bus, pci_device_fn)->irq; - ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; -#else - { - u8 pci_irq_line; - u32 pci_ioaddr; - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, - &pci_ioaddr); - irq = pci_irq_line; - ioaddr = pci_ioaddr; - } -#endif - /* Remove I/O space marker in bit 0. */ - ioaddr &= ~3; + /* 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; - printk(KERN_INFO "%s: %s at %#3lx,", - dev->name, tulip_tbl[chip_id].chip_name, ioaddr); + tp->next_module = root_tulip_dev; + root_tulip_dev = dev; + + pcibios_read_config_byte(pci_bus, pci_devfn, PCI_REVISION_ID, &chip_rev); + + /* Bring the 21041/21143 out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) + pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x00000000); + + printk(KERN_INFO "%s: %s rev %d at %#3lx,", + dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr); /* Stop the chip's Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* Clear the missed-packet counter. */ (volatile int)inl(ioaddr + CSR8); - if (chip_id == DC21041) { - if (inl(ioaddr + CSR9) & 0x8000) { - printk(" 21040 compatible mode,"); - chip_id = DC21040; - } else { - printk(" 21041 mode,"); - } + if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) { + printk(" 21040 compatible mode,"); + chip_idx = DC21040; } /* The station address ROM is read byte serially. The register must @@ -610,7 +712,7 @@ EEPROM. */ sum = 0; - if (chip_id == DC21040) { + if (chip_idx == DC21040) { outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ for (i = 0; i < 6; i++) { int value, boguscnt = 100000; @@ -620,28 +722,34 @@ dev->dev_addr[i] = value; sum += value & 0xff; } - } else if (chip_id == LC82C168) { + } else if (chip_idx == LC82C168) { for (i = 0; i < 3; i++) { int value, boguscnt = 100000; outl(0x600 | i, ioaddr + 0x98); do value = inl(ioaddr + CSR9); while (value < 0 && --boguscnt > 0); - ((u16*)dev->dev_addr)[i] = value; + put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); 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_ - but early vendor boards just put the address in the first six - EEPROM locations. */ - unsigned char ee_data[128]; + } else if (chip_idx == COMET) { + /* No need to read the EEPROM. */ + put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); + put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); + for (i = 0; i < 6; i ++) + sum += dev->dev_addr[i]; + } else { + /* A serial EEPROM interface, we read now and sort it out later. */ int sa_offset = 0; + int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; for (i = 0; i < sizeof(ee_data)/2; i++) - ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i); + ((u16 *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size)); - /* Detect the simple EEPROM format by the duplicated station addr. */ + /* DEC now has a specification (see Notes) but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ for (i = 0; i < 8; i ++) if (ee_data[i] != ee_data[16+i]) sa_offset = 20; @@ -655,7 +763,8 @@ } } /* Lite-On boards have the address byte-swapped. */ - if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00) + if ((dev->dev_addr[0] == 0xA0 || dev->dev_addr[0] == 0xC0) + && 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]; @@ -671,34 +780,37 @@ for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */ - irq = last_irq; +#if defined(__i386__) /* Patch up x86 BIOS bug. */ + if (last_irq) + irq = last_irq; #endif } for (i = 0; i < 6; i++) - printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); + printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); printk(", IRQ %d.\n", irq); last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ - /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ - request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); + /* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */ + request_region(ioaddr, tulip_tbl[chip_idx].io_size, dev->name); dev->base_addr = ioaddr; dev->irq = irq; - /* 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; - - tp->next_module = root_tulip_dev; - root_tulip_dev = dev; - tp->pci_bus = pci_bus; - tp->pci_dev_fn = pci_device_fn; - tp->chip_id = chip_id; + tp->pci_devfn = pci_devfn; + tp->chip_id = chip_idx; + tp->revision = chip_rev; + tp->flags = tulip_tbl[chip_idx].flags; + tp->csr0 = csr0; + + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. + And the ASIX must have a burst limit or horrible things happen. */ + if (chip_idx == DC21143 && chip_rev == 65) + tp->csr0 &= ~0x01000000; + else if (chip_idx == AX88140) + tp->csr0 |= 0x2000; #ifdef TULIP_FULL_DUPLEX tp->full_duplex = 1; @@ -729,37 +841,58 @@ if (tp->full_duplex) tp->full_duplex_lock = 1; - /* This is logically part of probe1(), but too complex to write inline. */ - if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) - parse_eeprom(dev); - 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; + } else if (tp->flags & HAS_8023X) + tp->to_advertise = 0x05e1; + else + tp->to_advertise = 0x01e1; + + /* This is logically part of probe1(), but too complex to write inline. */ + if (tp->flags & HAS_MEDIA_TABLE) { + memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom)); + parse_eeprom(dev); + } - if ((tp->mtable && tp->mtable->has_mii) || - ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { + if ((tp->flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tp->flags & HAS_MII))) { int phy, phy_idx; + if (tp->mtable && tp->mtable->has_mii) { + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == 11) { + tp->cur_index = i; + tp->saved_if_port = dev->if_port; + select_media(dev, 1); + dev->if_port = tp->saved_if_port; + break; + } + } /* 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(dev, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { int mii_reg0 = mdio_read(dev, phy, 0); + int mii_advert = mdio_read(dev, phy, 4); int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; tp->phys[phy_idx] = phy; tp->advertising[phy_idx++] = reg4; - printk(KERN_INFO "%s: MII transceiver found at MDIO address " - "%d, config %4.4x status %4.4x.\n", - dev->name, phy, mii_reg0, mii_status); - if (1 || (media_cap[tp->default_port] & MediaIsMII)) { + printk(KERN_INFO "%s: MII transceiver #%d " + "config %4.4x status %4.4x advertising %4.4x.\n", + dev->name, phy, mii_reg0, mii_status, mii_advert); + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != reg4) { printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," " previously advertising %4.4x.\n", - dev->name, reg4, phy, mdio_read(dev, phy, 4)); + dev->name, reg4, phy, mii_advert); + printk(KERN_DEBUG "%s: Advertising %4.4x (to advertise" + " is %4.4x).\n", + dev->name, reg4, tp->to_advertise); mdio_write(dev, phy, 4, reg4); } /* Enable autonegotiation: some boards default to off. */ @@ -788,9 +921,15 @@ dev->set_multicast_list = &set_rx_mode; #endif + if ((tp->flags & HAS_NWAY143) || tp->chip_id == DC21041) + tp->link_change = t21142_lnk_change; + else if (tp->flags & HAS_PNICNWAY) + tp->link_change = pnic_lnk_change; + /* Reset the xcvr interface and turn on heartbeat. */ - switch (chip_id) { + switch (chip_idx) { case DC21041: + tp->to_advertise = 0x0061; outl(0x00000000, ioaddr + CSR13); outl(0xFFFFFFFF, ioaddr + CSR14); outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ @@ -806,28 +945,43 @@ 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. */ + case PNIC2: + if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) { + outl(0x82020000, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x820E0000, ioaddr + CSR6); + } else + t21142_start_nway(dev); break; case LC82C168: if ( ! tp->mii_cnt) { + tp->nway = 1; + tp->nwayset = 0; 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: + case MX98713: case COMPEX9881: outl(0x00000000, ioaddr + CSR6); outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ outl(0x00000001, ioaddr + CSR13); break; + case MX98715: case MX98725: + outl(0x01a80000, ioaddr + CSR6); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00001000, ioaddr + CSR12); + break; + case COMET: + /* No initialization necessary. */ + break; } + if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) + pcibios_write_config_dword(pci_bus, pci_devfn, 0x40, 0x40000000); + return dev; } @@ -846,29 +1000,37 @@ } eeprom_fixups[] = { {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, - {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, 0x0000, 0x009E, /* 10baseT */ - 0x0903, 0x006D, /* 100baseTx */ }}, - {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0903, 0x006D, /* 100baseTx */ + 0x0905, 0x006D, /* 100baseTx-FD */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, 0x0107, 0x8021, /* 100baseFx */ 0x0108, 0x8021, /* 100baseFx-FD */ - 0x0103, 0x006D, /* 100baseTx */ }}, - {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, + 0x0100, 0x009E, /* 10baseT */ + 0x0104, 0x009E, /* 10baseT-FD */ + 0x0103, 0x006D, /* 100baseTx */ + 0x0105, 0x006D, /* 100baseTx-FD */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ 0x0000, 0x009E, /* 10baseT */ - 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }}, - {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F, - 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ - 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ - 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ + 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ }}, {0, 0, 0, 0, {}}}; static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; -#define EEPROM_SIZE 128 -#if defined(__i386__) +#if defined(__i386__) /* AKA get_unaligned() */ #define get_u16(ptr) (*(u16 *)(ptr)) #else #define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) @@ -881,14 +1043,10 @@ static unsigned char *last_ee_data = NULL; static int controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; - long ioaddr = dev->base_addr; unsigned char *ee_data = tp->eeprom; int i; tp->mtable = 0; - for (i = 0; i < EEPROM_SIZE/2; 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 ++) @@ -925,20 +1083,13 @@ } } if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ - printk(KERN_INFO "%s: Old style EEPROM -- no media selection information.\n", + printk(KERN_INFO "%s: Old style EEPROM with no media selection " + "information.\n", dev->name); return; } } - if (tulip_debug > 1) { - 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)); - } - printk("\n"); - } - + controller_index = 0; if (ee_data[19] > 1) { /* Multiport board. */ last_ee_data = ee_data; @@ -948,42 +1099,29 @@ 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; - int count; + int media = get_u16(p); + int count = p[2]; + p += 3; - 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, + printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n", + dev->name, media, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { unsigned char media_code = *p++; - u16 csrvals[3]; - int idx; - for (idx = 0; idx < 3; idx++) { - csrvals[idx] = 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]); + if (media_code & 0x40) + p += 6; + 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; + int count, new_advertise = 0; struct mediatable *mtable; u16 media = get_u16(p); p += 2; - if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) + if (tp->flags & CSR12_IN_SROM) csr12dir = *p++; count = *p++; mtable = (struct mediatable *) @@ -995,13 +1133,14 @@ mtable->defaultmedia = media; mtable->leafcount = count; mtable->csr12dir = csr12dir; - mtable->has_nonmii = mtable->has_mii = 0; + mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; + mtable->csr15dir = mtable->csr15val = 0; printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { struct medialeaf *leaf = &mtable->mleaf[i]; - + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ leaf->type = 0; leaf->media = p[0] & 0x3f; @@ -1011,12 +1150,34 @@ p += 4; } else { leaf->type = p[1]; - if (p[1] & 1) { + if (p[1] == 0x05) { + mtable->has_reset = i; + leaf->media = p[2] & 0x0f; + } else if (p[1] & 1) { mtable->has_mii = 1; leaf->media = 11; } else { mtable->has_nonmii = 1; leaf->media = p[2] & 0x0f; + switch (leaf->media) { + case 0: new_advertise |= 0x0020; break; + case 4: new_advertise |= 0x0040; break; + case 3: new_advertise |= 0x0080; break; + case 5: new_advertise |= 0x0100; break; + case 6: new_advertise |= 0x0200; break; + } + if (p[1] == 2 && leaf->media == 0) { + if (p[2] & 0x40) { + u32 base15 = get_unaligned((u16*)&p[7]); + mtable->csr15dir = + (get_unaligned((u16*)&p[9])<<16) + base15; + mtable->csr15val = + (get_unaligned((u16*)&p[11])<<16) + base15; + } else { + mtable->csr15dir = get_unaligned((u16*)&p[3])<<16; + mtable->csr15val = get_unaligned((u16*)&p[5])<<16; + } + } } leaf->leafdata = p + 2; p += (p[0] & 0x3f) + 1; @@ -1025,7 +1186,7 @@ unsigned char *bp = leaf->leafdata; printk(KERN_INFO "%s: MII interface PHY %d, setup/reset " "sequences %d/%d long, capabilities %2.2x %2.2x.\n", - dev->name, bp[0], bp[1], bp[1 + bp[1]*2], + dev->name, bp[0], bp[1], bp[2 + bp[1]*2], bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); } printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described " @@ -1033,6 +1194,8 @@ dev->name, i, medianame[leaf->media], leaf->media, block_name[leaf->type], leaf->type); } + if (new_advertise) + tp->to_advertise = new_advertise; } } /* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ @@ -1040,54 +1203,48 @@ /* EEPROM_Ctrl bits. */ #define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ #define EE_CS 0x01 /* EEPROM chip select. */ -#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_WRITE 0x04 /* Data from the Tulip to EEPROM. */ #define EE_WRITE_0 0x01 #define EE_WRITE_1 0x05 -#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_DATA_READ 0x08 /* Data from the EEPROM chip. */ #define EE_ENB (0x4800 | EE_CS) /* Delay between EEPROM clock transitions. - The 1.2 code is a "nasty" timing loop, but PC compatible machines are - *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro. */ -#ifdef _LINUX_DELAY_H -#define eeprom_delay(nanosec) udelay((nanosec + 999)/1000) -#else -#define eeprom_delay(nanosec) do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) -#endif + Even at 33Mhz current PCI implementations don't overrun the EEPROM clock. + We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ -#define EE_WRITE_CMD (5 << 6) -#define EE_READ_CMD (6 << 6) -#define EE_ERASE_CMD (7 << 6) +#define EE_READ_CMD (6) -static int read_eeprom(long ioaddr, int location) +/* Note: this routine returns extra data bits for size detection. */ +static int read_eeprom(long ioaddr, int location, int addr_len) { int i; - unsigned short retval = 0; + unsigned retval = 0; long ee_addr = ioaddr + CSR9; - int read_cmd = location | EE_READ_CMD; - + int read_cmd = location | (EE_READ_CMD << addr_len); + outl(EE_ENB & ~EE_CS, ee_addr); outl(EE_ENB, ee_addr); - + /* Shift the read command bits out. */ - for (i = 10; i >= 0; i--) { + for (i = 4 + addr_len; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outl(EE_ENB | dataval, ee_addr); - eeprom_delay(100); + eeprom_delay(); outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); - eeprom_delay(150); - outl(EE_ENB | dataval, ee_addr); /* Finish EEPROM a clock tick. */ - eeprom_delay(250); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); } outl(EE_ENB, ee_addr); - + for (i = 16; i > 0; i--) { outl(EE_ENB | EE_SHIFT_CLK, ee_addr); - eeprom_delay(100); + eeprom_delay(); retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); outl(EE_ENB, ee_addr); - eeprom_delay(100); + eeprom_delay(); } /* Terminate the EEPROM access. */ @@ -1121,19 +1278,33 @@ int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; int retval = 0; - long mdio_addr = dev->base_addr + CSR9; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; if (tp->chip_id == LC82C168) { - long ioaddr = dev->base_addr; int i = 1000; outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); while (--i > 0) if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) return retval & 0xffff; return 0xffff; } - /* Establish sync by sending at least 32 logic ones. */ + if (tp->chip_id == COMET) { + if (phy_id == 1) { + if (location < 7) + return inl(ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + return inl(ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + return inl(ioaddr + 0xD4 + ((location-29)<<2)); + } + return 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1165,10 +1336,10 @@ struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; - long mdio_addr = dev->base_addr + CSR9; + long ioaddr = dev->base_addr; + long mdio_addr = ioaddr + CSR9; if (tp->chip_id == LC82C168) { - long ioaddr = dev->base_addr; int i = 1000; outl(cmd, ioaddr + 0xA0); do @@ -1178,7 +1349,19 @@ return; } - /* Establish sync by sending 32 logic ones. */ + if (tp->chip_id == COMET) { + if (phy_id != 1) + return; + if (location < 7) + outl(value, ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + outl(value, ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + outl(value, ioaddr + 0xD4 + ((location-29)<<2)); + return; + } + + /* Establish sync by sending 32 logic ones. */ for (i = 32; i >= 0; i--) { outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); mdio_delay(); @@ -1209,7 +1392,12 @@ { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; - int i = 0; + int next_tick = 3*HZ; + int i; + + /* Wake the chip from sleep/snooze mode. */ + if (tp->flags & HAS_PWRDWN) + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0); /* On some chip revs we must set the MII/SYM port before the reset!? */ if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) @@ -1217,86 +1405,61 @@ /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ outl(0x00000001, ioaddr + CSR0); -#ifdef _LINUX_DELAY_H - udelay(2); -#else - SLOW_DOWN_IO; -#endif + + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + MOD_INC_USE_COUNT; + /* Deassert reset. - 486: Set 8 longword cache alignment, 8 longword burst. - 586: Set 16 longword cache alignment, no burst limit. - Cache alignment bits 15:14 Burst length 13:8 - 0000 No alignment 0x00000000 unlimited 0800 8 longwords - 4000 8 longwords 0100 1 longword 1000 16 longwords - 8000 16 longwords 0200 2 longwords 2000 32 longwords - C000 32 longwords 0400 4 longwords Wait the specified 50 PCI cycles after a reset by initializing Tx and Rx queues and the address filter list. */ -#if defined(__alpha__) - /* ToDo: Alpha setting could be better. */ - 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(0x01A00000 | 0x4800, ioaddr + CSR0); -#else -#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, - 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); -#endif -#else - outl(0x01A00000 | 0x4800, ioaddr + CSR0); -#warning Processor architecture undefined! -#endif - -#ifdef SA_SHIRQ - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { - return -EAGAIN; - } -#else - if (irq2dev_map[dev->irq] != NULL - || (irq2dev_map[dev->irq] = dev) == NULL - || dev->irq == 0 - || request_irq(dev->irq, &tulip_interrupt, 0, - tulip_tbl[tp->chip_id].chip_name)) { - return -EAGAIN; - } -#endif + outl(tp->csr0, ioaddr + CSR0); if (tulip_debug > 1) printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); - MOD_INC_USE_COUNT; - tulip_init_ring(dev); - /* This is set_rx_mode(), but without starting the transmitter. */ - /* Fill the whole address filter table with our physical address. */ - { +#if 0 + if (tp->chip_id == PNIC2) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4))); + addr_high = (dev->dev_addr[4]<<8) + (dev->dev_addr[5]<<0); + outl((dev->dev_addr[0]<<8) + dev->dev_addr[1] + + (dev->dev_addr[2]<<24) + (dev->dev_addr[3]<<16), + ioaddr + 0xB0); + outl(addr_high + (addr_high<<16), ioaddr + 0xB8); + } +#endif + if (tp->flags & MC_HASH_ONLY) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4))); + if (tp->chip_id == AX88140) { + outl(0, ioaddr + CSR13); + outl(addr_low, ioaddr + CSR14); + outl(1, ioaddr + CSR13); + outl(addr_high, ioaddr + CSR14); + } else if (tp->chip_id == COMET) { + outl(addr_low, ioaddr + 0xA4); + outl(addr_high, ioaddr + 0xA8); + outl(0, ioaddr + 0xAC); + outl(0, ioaddr + 0xB0); + } + } else { + /* This is set_rx_mode(), but without starting the transmitter. */ u16 *eaddrs = (u16 *)dev->dev_addr; - u32 *setup_frm = tp->setup_frame, i; + u16 *setup_frm = &tp->setup_frame[15*6]; - /* You must add the broadcast address when doing perfect filtering! */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - /* Fill the rest of the accept table with our physical address. */ - for (i = 1; i < 16; i++) { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; - } + /* 21140 bug: you must add the broadcast address. */ + memset(tp->setup_frame, 0xff, sizeof(tp->setup_frame)); + /* Fill the final entry of the table with our physical address. */ + *setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; /* Put the setup frame on the Tx list. */ tp->tx_ring[0].length = 0x08000000 | 192; tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].status = 0x80000000; + tp->tx_ring[0].status = DescOwned; tp->cur_tx++; } @@ -1304,13 +1467,12 @@ outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + tp->saved_if_port = dev->if_port; if (dev->if_port == 0) dev->if_port = tp->default_port; - if (tp->chip_id == DC21041 && dev->if_port > 4) - /* Invalid: Select initial TP, autosense, autonegotiate. */ - dev->if_port = 4; /* Allow selecting a default media. */ + i = 0; if (tp->mtable == NULL) goto media_picked; if (dev->if_port) { @@ -1323,31 +1485,80 @@ goto media_picked; } } - if ((tp->mtable->defaultmedia & 0x0800) == 0) + if ((tp->mtable->defaultmedia & 0x0800) == 0) { + int looking_for = tp->mtable->defaultmedia & 15; for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { - printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", - dev->name, medianame[tp->mtable->mleaf[i].media]); - goto media_picked; - } + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", + dev->name, medianame[looking_for]); + goto media_picked; + } + } /* Start sensing first non-full-duplex media. */ for (i = tp->mtable->leafcount - 1; (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) - ; + ; media_picked: tp->csr6 = 0; tp->cur_index = i; + tp->nwayset = 0; + if (dev->if_port == 0 && tp->chip_id == DC21041) { + tp->nway = 1; + } if (dev->if_port == 0 && tp->chip_id == DC21142) { - tp->csr6 = 0x82420200; - outl(0x0003FFFF, ioaddr + CSR14); + if (tp->mii_cnt) { + select_media(dev, 1); + if (tulip_debug > 1) + printk(KERN_INFO "%s: Using MII transceiver %d, status " + "%4.4x.\n", + dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); + outl(0x82020000, ioaddr + CSR6); + tp->csr6 = 0x820E0000; + dev->if_port = 11; + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + } else + t21142_start_nway(dev); + } else if (tp->chip_id == PNIC2) { + t21142_start_nway(dev); + } else if (tp->chip_id == LC82C168 && ! tp->medialock) { + if (tp->mii_cnt) { + dev->if_port = 11; + tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else if (inl(ioaddr + CSR5) & TPLnkPass) + pnic_do_nway(dev); + else { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + tp->csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + next_tick = 1*HZ; + } + } else if ((tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) + && ! tp->medialock) { + dev->if_port = 0; + tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { + /* Provided by BOLO, Macronix - 12/10/1998. */ + dev->if_port = 0; + tp->csr6 = 0x01880200; + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); + } else if (tp->chip_id == DC21143 && + media_cap[dev->if_port] & MediaIsMII) { + /* We must reset the media CSRs when we force-select MII mode. */ + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); outl(0x0008, ioaddr + CSR15); - outl(0x0001, ioaddr + CSR13); - outl(0x1301, ioaddr + CSR12); - } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { - dev->if_port = 11; - tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); - outl(0x0001, ioaddr + CSR15); + } else if (tp->chip_id == COMET) { + dev->if_port = 0; + tp->csr6 = 0x00040000; + } else if (tp->chip_id == AX88140) { + tp->csr6 = 0x00000100; } else select_media(dev, 1); @@ -1373,7 +1584,7 @@ /* 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(5*HZ); + tp->timer.expires = RUN_AT(next_tick); tp->timer.data = (unsigned long)dev; tp->timer.function = tulip_tbl[tp->chip_id].media_timer; add_timer(&tp->timer); @@ -1388,7 +1599,7 @@ struct tulip_private *tp = (struct tulip_private *)dev->priv; struct mediatable *mtable = tp->mtable; u32 new_csr6; - int check_mii =0, i; + int i; if (mtable) { struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; @@ -1406,33 +1617,56 @@ new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); break; case 2: case 4: { - u16 setup[3]; - for (i = 0; i < 3; i++) + u16 setup[5]; + u32 csr13val, csr14val, csr15dir, csr15val; + for (i = 0; i < 5; i++) setup[i] = get_u16(&p[i*2 + 1]); dev->if_port = p[0] & 15; + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Resetting the transceiver.\n", + dev->name); + for (i = 0; i < rst[0]; i++) + outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } if (tulip_debug > 1) - printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", + printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control " + "%4.4x/%4.4x.\n", dev->name, medianame[dev->if_port], setup[0], setup[1]); if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + csr13val = setup[0]; + csr14val = setup[1]; + csr15dir = (setup[3]<<16) | setup[2]; + csr15val = (setup[4]<<16) | setup[2]; outl(0, ioaddr + CSR13); - outl(setup[1], ioaddr + CSR14); - outl(setup[2], ioaddr + CSR15); - outl(setup[0], ioaddr + CSR13); - 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); + outl(csr14val, ioaddr + CSR14); + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + outl(csr13val, ioaddr + CSR13); } else { - outl(0, ioaddr + CSR14); - outl(8, ioaddr + CSR15); - outl(0, ioaddr + CSR13); + csr13val = 1; + csr14val = 0x0003FF7F; + csr15dir = (setup[0]<<16) | 0x0008; + csr15val = (setup[1]<<16) | 0x0008; + if (dev->if_port <= 4) + csr14val = t21142_csr14[dev->if_port]; + if (startup) { + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + } + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + if (startup) outl(csr13val, ioaddr + CSR13); } - outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ - outl(setup[1]<<16, ioaddr + CSR15); /* Data */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Setting CSR15 to %8.8x/%8.8x.\n", + dev->name, csr15dir, csr15val); if (mleaf->type == 4) new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); else @@ -1446,7 +1680,6 @@ u16 to_advertise; dev->if_port = 11; - check_mii = 1; new_csr6 = 0x020E0000; if (mleaf->type == 3) { /* 21142 */ u16 *init_sequence = (u16*)(p+2); @@ -1473,7 +1706,7 @@ } to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; tp->advertising[phy_num] = to_advertise; - if (tulip_debug > 1 || 1) + if (tulip_debug > 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? */ @@ -1481,32 +1714,33 @@ break; } default: - new_csr6 = 0x020E0000; + printk(KERN_DEBUG "%s: Invalid media table selection %d.\n", + dev->name, mleaf->type); + 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 == DC21041) { + int port = dev->if_port <= 4 ? dev->if_port : 0; if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", - dev->name, medianame[dev->if_port & 15], - inl(ioaddr + CSR12) & 0xffff); + dev->name, medianame[port == 3 ? 12: port], + inl(ioaddr + CSR12)); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - outl(t21041_csr14[dev->if_port], ioaddr + CSR14); - outl(t21041_csr15[dev->if_port], ioaddr + CSR15); - outl(t21041_csr13[dev->if_port], ioaddr + CSR13); + outl(t21041_csr14[port], ioaddr + CSR14); + outl(t21041_csr15[port], ioaddr + CSR15); + outl(t21041_csr13[port], ioaddr + CSR13); new_csr6 = 0x80020000; } else if (tp->chip_id == LC82C168) { if (startup && ! tp->medialock) dev->if_port = tp->mii_cnt ? 11 : 0; if (tulip_debug > 1) - printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," - " media %s.\n", - dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), - medianame[dev->if_port]); + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n", + dev->name, inl(ioaddr + 0xB8), medianame[dev->if_port]); if (tp->mii_cnt) { - new_csr6 = 0x812C0000; + new_csr6 = 0x810C0000; outl(0x0001, ioaddr + CSR15); outl(0x0201B07A, ioaddr + 0xB8); } else if (startup) { @@ -1518,10 +1752,8 @@ } 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); + /* Trigger autonegotiation. */ + outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8); } else { outl(0x32, ioaddr + CSR12); new_csr6 = 0x00420000; @@ -1532,18 +1764,24 @@ int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", - dev->name, dev->if_port ? "AUI" : "10baseT", csr12); - new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000); + dev->name, medianame[dev->if_port], csr12); + if (media_cap[dev->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + new_csr6 = 0x20000; /* Set the full duplux match frame. */ outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + if (t21040_csr13[dev->if_port] & 8) { + outl(0x0705, ioaddr + CSR14); + outl(0x0006, ioaddr + CSR15); + } else { + outl(0xffff, ioaddr + CSR14); + outl(0x0000, ioaddr + CSR15); + } + outl(0x8f01 | t21040_csr13[dev->if_port], 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; + dev->if_port = tp->mii_cnt ? 11 : 3; if (media_cap[dev->if_port] & MediaIsMII) { new_csr6 = 0x020E0000; } else if (media_cap[dev->if_port] & MediaIsFx) { @@ -1561,28 +1799,80 @@ return; } +/* + Check the MII negotiated duplex, and change the CSR6 setting if + required. + Return 0 if everything is OK. + Return < 0 if the transceiver is missing or has no link beat. + */ +static int check_duplex(struct device *dev) +{ + long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int mii_reg1, mii_reg5, negotiated, duplex; + + if (tp->full_duplex_lock) + return 0; + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x.\n", dev->name, mii_reg1, mii_reg5); + if (mii_reg1 == 0xffff) + return -2; + if ((mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + if (tulip_debug > 1) + printk(KERN_INFO "%s: No link beat on the MII interface," + " status %4.4x.\n", dev->name, new_reg1); + return -1; + } + } + negotiated = mii_reg5 & tp->advertising[0]; + duplex = ((negotiated & 0x0300) == 0x0100 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (negotiated & 0x038) /* 100mbps. */ + tp->csr6 &= ~0x00400000; + if (tp->full_duplex) tp->csr6 |= 0x0200; + else tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (tulip_debug > 0) + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + "#%d link partner capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + return 1; + } + return 0; +} + static void tulip_timer(unsigned long data) { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; u32 csr12 = inl(ioaddr + CSR12); - int next_tick = 0; + int next_tick = 2*HZ; - if (tulip_debug > 3) { - printk(KERN_DEBUG "%s: Media selection tick, status %8.8x mode %8.8x " - "SIA %8.8x %8.8x %8.8x %8.8x.\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6), - csr12, inl(ioaddr + CSR13), + if (tulip_debug > 2) { + printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode" + " %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n", + dev->name, medianame[dev->if_port], inl(ioaddr + CSR5), + inl(ioaddr + CSR6), csr12, inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); } switch (tp->chip_id) { case DC21040: - if (csr12 & 0x0002) { /* Network error */ - printk(KERN_INFO "%s: No 10baseT link beat found, switching to %s media.\n", - dev->name, dev->if_port ? "10baseT" : "AUI"); - dev->if_port ^= 1; - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + if (!tp->medialock && csr12 & 0x0002) { /* Network error */ + printk(KERN_INFO "%s: No link beat found.\n", + dev->name); + dev->if_port = (dev->if_port == 2 ? 0 : 2); + select_media(dev, 0); dev->trans_start = jiffies; } break; @@ -1590,6 +1880,7 @@ if (tulip_debug > 2) printk(KERN_DEBUG "%s: 21041 media tick CSR12 %8.8x.\n", dev->name, csr12); + if (tp->medialock) break; switch (dev->if_port) { case 0: case 3: case 4: if (csr12 & 0x0004) { /*LnkFail */ @@ -1611,25 +1902,26 @@ break; case 1: /* 10base2 */ case 2: /* AUI */ - if (csr12 & 0x0100) { - next_tick = (30*HZ); /* 30 sec. */ - tp->mediasense = 0; - } else if ((csr12 & 0x0004) == 0) { - printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", dev->name); - dev->if_port = 0; - select_media(dev, 0); - next_tick = (24*HZ)/10; /* 2.4 sec. */ - } else if (tp->mediasense || (csr12 & 0x0002)) { - dev->if_port = 3 - dev->if_port; /* Swap ports. */ - select_media(dev, 0); - next_tick = 20*HZ; - } else { - next_tick = 20*HZ; - } - break; + if (csr12 & 0x0100) { + next_tick = (30*HZ); /* 30 sec. */ + tp->mediasense = 0; + } else if ((csr12 & 0x0004) == 0) { + printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", + dev->name); + dev->if_port = 0; + select_media(dev, 0); + next_tick = (24*HZ)/10; /* 2.4 sec. */ + } else if (tp->mediasense || (csr12 & 0x0002)) { + dev->if_port = 3 - dev->if_port; /* Swap ports. */ + select_media(dev, 0); + next_tick = 20*HZ; + } else { + next_tick = 20*HZ; + } + break; } break; - case DC21140: case DC21142: case MX98713: default: { + case DC21140: case DC21142: case MX98713: case COMPEX9881: default: { struct medialeaf *mleaf; unsigned char *p; if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ @@ -1698,53 +1990,11 @@ next_tick = (24*HZ)/10; break; } - case 1: case 3: { /* 21140, 21142 MII */ - int mii_reg1, mii_reg5; + case 1: case 3: /* 21140, 21142 MII */ actually_mii: - mii_reg1 = mdio_read(dev, tp->phys[0], 1); - mii_reg5 = mdio_read(dev, tp->phys[0], 5); - if (tulip_debug > 1) - printk(KERN_INFO "%s: MII status %4.4x, Link partner report " - "%4.4x, 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(dev, tp->phys[0], 1); - if ((new_reg1 & 0x0004) == 0) { - printk(KERN_INFO "%s: No link beat on the MII interface," - " status then %4.4x now %4.4x.\n", - dev->name, mii_reg1, new_reg1); - if (tp->mtable && tp->mtable->has_nonmii) - goto select_next_media; - } - } - 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); - } - } + check_duplex(dev); next_tick = 60*HZ; break; - } case 2: /* 21142 serial block has no link beat. */ default: break; @@ -1752,10 +2002,8 @@ } break; } - if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); - } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); } /* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list @@ -1769,49 +2017,50 @@ int next_tick = 60*HZ; int new_csr6 = 0; - if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + if (tulip_debug > 2) + printk(KERN_INFO"%s: 21143 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. */ + if (media_cap[dev->if_port] & MediaIsMII) { + check_duplex(dev); + next_tick = 60*HZ; + } else if (tp->nwayset) { + /* Don't screw up a negotiated session! */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n", + dev->name, medianame[dev->if_port], csr12); + } else if (tp->medialock) { + ; + } else if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, " + "trying NWay.\n", dev->name, csr12); + t21142_start_nway(dev); + next_tick = 3*HZ; } } else if ((csr12 & 0x7000) != 0x5000) { /* Negotiation failed. Search media types. */ if (tulip_debug > 1) - printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n", + printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", dev->name, csr12); if (!(csr12 & 4)) { /* 10mbps link beat good. */ new_csr6 = 0x82420000; dev->if_port = 0; outl(0, ioaddr + CSR13); outl(0x0003FFFF, ioaddr + CSR14); - outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outw(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); + outw(8, ioaddr + CSR15); outl(1, ioaddr + CSR13); } if (tulip_debug > 1) - printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + printk(KERN_INFO"%s: Testing new 21143 media %s.\n", dev->name, medianame[dev->if_port]); if (new_csr6 != (tp->csr6 & ~0x00D5)) { tp->csr6 &= 0x00D5; @@ -1820,51 +2069,147 @@ outl(tp->csr6 | 0x0002, ioaddr + CSR6); outl(tp->csr6 | 0x2002, ioaddr + CSR6); } + next_tick = 3*HZ; + } + if (tp->cur_tx - tp->dirty_tx > 0 && + jiffies - dev->trans_start > TX_TIMEOUT) { + printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", + dev->name, tp->cur_tx, tp->dirty_tx); + tulip_tx_timeout(dev); } + tp->timer.expires = RUN_AT(next_tick); add_timer(&tp->timer); } -static void t21142_lnk_change( struct device *dev) +static void t21142_start_nway(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr14 = ((tp->to_advertise & 0x0780) << 9) | + ((tp->to_advertise&0x0020)<<1) | 0xffbf; + + dev->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + if (debug > 1) + printk(KERN_DEBUG "%s: Restarting 21143 autonegotiation, %8.8x.\n", + dev->name, csr14); + outl(0x0001, ioaddr + CSR13); + outl(csr14, ioaddr + CSR14); + tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0); + outl(tp->csr6, ioaddr + CSR6); + if (tp->mtable && tp->mtable->csr15dir) { + outl(tp->mtable->csr15dir, ioaddr + CSR15); + outl(tp->mtable->csr15val, ioaddr + CSR15); + } else + outw(0x0008, ioaddr + CSR15); + outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */ +} + +static void t21142_lnk_change(struct device *dev, int csr5) { struct tulip_private *tp = (struct tulip_private *)dev->priv; long 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)); + printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " + "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14)); - 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 { + /* If NWay finished and we have a negotiated partner capability. */ + if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { + int setup_done = 0; + int negotiated = tp->to_advertise & (csr12 >> 16); + tp->lpar = csr12 >> 16; + tp->nwayset = 1; + if (negotiated & 0x0100) dev->if_port = 5; + else if (negotiated & 0x0080) dev->if_port = 3; + else if (negotiated & 0x0040) dev->if_port = 4; + else if (negotiated & 0x0020) dev->if_port = 0; + else { + tp->nwayset = 0; + if ((csr12 & 2) == 0 && (tp->to_advertise & 0x0180)) 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", + } + tp->full_duplex = (media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0; + + if (tulip_debug > 1) { + if (tp->nwayset) + printk(KERN_INFO "%s: Switching to %s based on link " + "negotiation %4.4x & %4.4x = %4.4x.\n", + dev->name, medianame[dev->if_port], tp->to_advertise, + tp->lpar, negotiated); + else + printk(KERN_INFO "%s: Autonegotiation failed, using %s," + " link beat status %4.4x.\n", + dev->name, medianame[dev->if_port], csr12); + } + + if (tp->mtable) { + int i; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == dev->if_port) { + tp->cur_index = i; + select_media(dev, 0); + setup_done = 1; + break; + } + } + if ( ! setup_done) { + tp->csr6 = dev->if_port & 1 ? 0x83860000 : 0x82420000; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + outl(1, ioaddr + CSR13); + } +#if 0 /* Restart shouldn't be needed. */ + outl(tp->csr6 | 0x0000, ioaddr + CSR6); + if (debug > 2) + printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n", + dev->name, inl(ioaddr + CSR5)); +#endif + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (debug > 2) + printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n", + dev->name, tp->csr6, inl(ioaddr + CSR6), + inl(ioaddr + CSR12)); + } else if ((tp->nwayset && (csr5 & 0x08000000) + && (dev->if_port == 3 || dev->if_port == 5) + && (csr12 & 2) == 2) || + (tp->nway && (csr5 & (TPLnkFail)))) { + /* Link blew? Maybe restart NWay. */ + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } else if (dev->if_port == 3 || dev->if_port == 5) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21143 %s link beat %s.\n", + dev->name, medianame[dev->if_port], + (csr12 & 2) ? "failed" : "good"); + if ((csr12 & 2) && ! tp->medialock) { + del_timer(&tp->timer); + t21142_start_nway(dev); + tp->timer.expires = RUN_AT(3*HZ); + add_timer(&tp->timer); + } + } else if (dev->if_port == 0 || dev->if_port == 4) { + if ((csr12 & 4) == 0) + printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", dev->name); } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ - printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + if (tulip_debug) + printk(KERN_INFO"%s: 21143 10mbps sensed media.\n", + dev->name); + dev->if_port = 0; + } else if (tp->nwayset) { + if (tulip_debug) + printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n", + dev->name, medianame[dev->if_port], tp->csr6); + } else { /* 100mbps link beat good. */ + if (tulip_debug) + printk(KERN_INFO"%s: 21143 100baseTx 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); @@ -1873,7 +2218,6 @@ outl(tp->csr6 | 0x2002, ioaddr + CSR6); } } - static void mxic_timer(unsigned long data) { @@ -1892,57 +2236,98 @@ } } +static void pnic_do_nway(struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u32 phy_reg = inl(ioaddr + 0xB8); + u32 new_csr6 = tp->csr6 & ~0x40C40200; + + if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + if (phy_reg & 0x20000000) dev->if_port = 5; + else if (phy_reg & 0x40000000) dev->if_port = 3; + else if (phy_reg & 0x10000000) dev->if_port = 4; + else if (phy_reg & 0x08000000) dev->if_port = 0; + tp->nwayset = 1; + new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000; + outl(0x32 | (dev->if_port & 1), ioaddr + CSR12); + if (dev->if_port & 1) + outl(0x1F868, ioaddr + 0xB8); + if (phy_reg & 0x30000000) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n", + dev->name, phy_reg, medianame[dev->if_port]); + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + } + } +} +static void pnic_lnk_change(struct device *dev, int csr5) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int phy_reg = inl(ioaddr + 0xB8); + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n", + dev->name, phy_reg, csr5); + if (inl(ioaddr + CSR5) & TPLnkFail) { + outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7); + if (! tp->nwayset || jiffies - dev->trans_start > 1*HZ) { + tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff); + outl(tp->csr6, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + dev->trans_start = jiffies; + } + } else if (inl(ioaddr + CSR5) & TPLnkPass) { + pnic_do_nway(dev); + outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7); + } +} static void pnic_timer(unsigned long data) { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; - int csr12 = inl(ioaddr + CSR12); int next_tick = 60*HZ; - int new_csr6 = tp->csr6 & ~0x40C40200; if (media_cap[dev->if_port] & MediaIsMII) { - int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; - - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, " - "CSR5 %8.8x.\n", - dev->name, negotiated, inl(ioaddr + CSR5)); - - if (negotiated & 0x0380) /* 10 vs 100mbps */ - new_csr6 |= 0x812E0000; - else - new_csr6 |= 0x816E0000; - if (((negotiated & 0x0300) == 0x0100) /* Duplex */ - || (negotiated & 0x00C0) == 0x0040 - || tp->full_duplex_lock) { - tp->full_duplex = 1; - new_csr6 |= 0x0200; - } - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link " - "partner report %4.4x, csr6 %8.8x/%8.8x.\n", - dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, - tp->csr6, inl(ioaddr + CSR6)); + if (check_duplex(dev) > 0) + next_tick = 3*HZ; } else { + int csr12 = inl(ioaddr + CSR12); + int new_csr6 = tp->csr6 & ~0x40C40200; int phy_reg = inl(ioaddr + 0xB8); int csr5 = inl(ioaddr + CSR5); if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", - dev->name, phy_reg, csr5); - + printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s " + "CSR5 %8.8x.\n", + dev->name, phy_reg, medianame[dev->if_port], csr5); if (phy_reg & 0x04000000) { /* Remote link fault */ - /*outl(0x0201F078, ioaddr + 0xB8);*/ - next_tick = 3*HZ; - } - if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ + outl(0x0201F078, ioaddr + 0xB8); + next_tick = 1*HZ; + tp->nwayset = 0; + } else if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + pnic_do_nway(dev); + next_tick = 60*HZ; + } else if (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)); + next_tick = 3*HZ; if (tp->medialock) { + } else if (tp->nwayset && (dev->if_port & 1)) { + next_tick = 1*HZ; } else if (dev->if_port == 0) { dev->if_port = 3; outl(0x33, ioaddr + CSR12); @@ -1954,121 +2339,144 @@ new_csr6 = 0x00420000; outl(0x1F078, ioaddr + 0xB8); } - new_csr6 |= (tp->csr6 & 0xfdff); - next_tick = 3*HZ; - } else - new_csr6 = tp->csr6; - if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { - tp->full_duplex = 1; - new_csr6 |= 0x00000200; + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + if (tulip_debug > 1) + printk(KERN_INFO "%s: Changing PNIC configuration to %s " + "%s-duplex, CSR6 %8.8x.\n", + dev->name, medianame[dev->if_port], + tp->full_duplex ? "full" : "half", new_csr6); + } } } - if (tp->csr6 != new_csr6) { - tp->csr6 = new_csr6; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - dev->trans_start = jiffies; - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " - "CSR6 %8.8x.\n", - dev->name, tp->full_duplex ? "full" : "half", new_csr6); - } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void comet_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " + "%4.4x.\n", + dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8)); 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; - long ioaddr = dev->base_addr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; - if (media_cap[dev->if_port] & MediaIsMII) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21040) { - if (inl(ioaddr + CSR12) & 0x0002) { - printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "10baseT" : "AUI"); - dev->if_port ^= 1; - outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); - } - dev->trans_start = jiffies; - return; - } else if (tp->chip_id == DC21041) { - u32 csr12 = inl(ioaddr + CSR12); + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + } else if (tp->chip_id == DC21040) { + if ( !tp->medialock && inl(ioaddr + CSR12) & 0x0002) { + dev->if_port = (dev->if_port == 2 ? 0 : 2); + printk(KERN_INFO "%s: transmit timed out, switching to " + "%s.\n", + dev->name, medianame[dev->if_port]); + select_media(dev, 0); + } + dev->trans_start = jiffies; + return; + } else if (tp->chip_id == DC21041) { + int csr12 = inl(ioaddr + CSR12); + + printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " + "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), csr12, + inl(ioaddr + CSR13), inl(ioaddr + CSR14)); + tp->mediasense = 1; + if ( ! tp->medialock) { + if (dev->if_port == 1 || dev->if_port == 2) + if (csr12 & 0x0004) { + dev->if_port = 2 - dev->if_port; + } else + dev->if_port = 0; + else + dev->if_port = 1; + select_media(dev, 0); + } + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713 || tp->chip_id == COMPEX9881) { + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if ( ! tp->medialock && tp->mtable) { + do + --tp->cur_index; + while (tp->cur_index >= 0 + && (media_cap[tp->mtable->mleaf[tp->cur_index].media] + & MediaIsFD)); + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s " + "media.\n", dev->name, medianame[dev->if_port]); + } + } else { + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " + "%8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); + dev->if_port = 0; + } + +#if defined(way_too_many_messages) + if (tulip_debug > 3) { + int i; + for (i = 0; i < RX_RING_SIZE; i++) { + u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); + int j; + printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x " + "%2.2x %2.2x %2.2x.\n", + i, (unsigned int)tp->rx_ring[i].status, + (unsigned int)tp->rx_ring[i].length, + (unsigned int)tp->rx_ring[i].buffer1, + (unsigned int)tp->rx_ring[i].buffer2, + buf[0], buf[1], buf[2]); + for (j = 0; buf[j] != 0xee && j < 1600; j++) + if (j < 100) printk(" %2.2x", buf[j]); + printk(" j=%d.\n", j); + } + printk(KERN_DEBUG " Rx ring %8.8x: ", (int)tp->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %8.8x: ", (int)tp->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); + printk("\n"); + } +#endif + + /* Stop and restart the chip's Tx processes . */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + /* Trigger an immediate transmit demand. */ + outl(0, ioaddr + CSR1); - printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x," - " CSR13 %8.8x, CSR14 %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), csr12, - inl(ioaddr + CSR13), inl(ioaddr + CSR14)); - tp->mediasense = 1; - if (dev->if_port == 1 || dev->if_port == 2) - if (csr12 & 0x0004) { - dev->if_port = 2 - dev->if_port; - } else - dev->if_port = 0; - else - dev->if_port = 1; - select_media(dev, 0); - tp->stats.tx_errors++; dev->trans_start = jiffies; + tp->stats.tx_errors++; return; - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 - || tp->chip_id == MX98713) { - /* Stop the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), - inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - if (tp->mtable) { - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - select_media(dev, 0); - printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - } - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - tp->stats.tx_errors++; - dev->trans_start = jiffies; - return; - } else - printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," - " resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12)); -#ifdef way_too_many_messages - printk(" Rx ring %8.8x: ", (int)tp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); - printk("\n Tx ring %8.8x: ", (int)tp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); - printk("\n"); -#endif - - /* Perhaps we should reinitialize the hardware here. */ - dev->if_port = 0; - /* Stop and restart the chip's Tx processes . */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - /* Trigger an immediate transmit demand. */ - outl(0, ioaddr + CSR1); - - dev->trans_start = jiffies; - tp->stats.tx_errors++; - return; } /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void -tulip_init_ring(struct device *dev) +static void tulip_init_ring(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; @@ -2078,30 +2486,30 @@ tp->dirty_rx = tp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */ + tp->rx_ring[i].status = 0x00000000; tp->rx_ring[i].length = 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. */ -#if LINUX_VERSION_CODE > 0x10300 - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); -#else - tp->rx_ring[i].buffer1 = virt_to_bus(skb->data); -#endif - } tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_skbuff[i] = NULL; } /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000; + tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP; tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + /* 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 = dev_alloc_skb(PKT_BUF_SZ); + tp->rx_skbuff[i] = skb; + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ + tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); + } + tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + /* 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++) { @@ -2138,68 +2546,54 @@ tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ - flag = 0x60000000; /* No interrupt */ - dev->tbusy = 0; + flag = 0x60000000; /* No interrupt */ } else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) { - flag = 0xe0000000; /* Tx-done intr. */ - dev->tbusy = 0; + flag = 0xe0000000; /* Tx-done intr. */ } else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) { - flag = 0x60000000; /* No Tx-done intr. */ - dev->tbusy = 0; - } else { - /* Leave room for set_rx_mode() to fill entries. */ - flag = 0xe0000000; /* Tx-done intr. */ - tp->tx_full = 1; + flag = 0x60000000; /* No Tx-done intr. */ + } else { /* Leave room for set_rx_mode() to fill entries. */ + tp->tx_full = 1; + flag = 0xe0000000; /* Tx-done intr. */ } if (entry == TX_RING_SIZE-1) - flag |= 0xe2000000; + flag |= 0xe0000000 | DESC_RING_WRAP; tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ + tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */ tp->cur_tx++; - /* Trigger an immediate transmit demand. */ - outl(0, dev->base_addr + CSR1); + if ( ! tp->tx_full) + clear_bit(0, (void*)&dev->tbusy); dev->trans_start = jiffies; + /* Trigger an immediate transmit demand. */ + outl(0, dev->base_addr + CSR1); return 0; } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs) +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { -#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = (struct device *)dev_instance; -#else - struct device *dev = (struct device *)(irq2dev_map[irq]); -#endif - - struct tulip_private *tp; - long ioaddr; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; int csr5, work_budget = max_interrupt_work; - if (dev == NULL) { - printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", - irq); +#if defined(__i386__) && defined(SMP_CHECK) + if (test_and_set_bit(0, (void*)&dev->interrupt)) { + printk(KERN_ERR "%s: Duplicate entry of the interrupt handler by " + "processor %d.\n", + dev->name, hard_smp_processor_id()); + dev->interrupt = 0; return; } - - ioaddr = dev->base_addr; - 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 + if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); -#endif return; } dev->interrupt = 1; -#ifdef SMP_CHECK - tp->smp_proc_id = hard_smp_processor_id(); #endif do { @@ -2260,11 +2654,7 @@ } /* Free the original skb. */ -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[entry]); -#else - dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[entry]); tp->tx_skbuff[entry] = 0; } @@ -2281,15 +2671,15 @@ /* The ring is no longer full, clear tbusy. */ tp->tx_full = 0; dev->tbusy = 0; - mark_bh(NET_BH); + netif_wake_queue(dev); } tp->dirty_tx = dirty_tx; if (csr5 & TxDied) { - if (tulip_debug > 1) - printk(KERN_WARNING "%s: The transmitter stopped!" - " CSR5 is %x, CSR6 %x.\n", - dev->name, csr5, inl(ioaddr + CSR6)); + if (tulip_debug > 2) + printk(KERN_WARNING "%s: The transmitter stopped." + " CSR5 is %x, CSR6 %x, new CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6), tp->csr6); outl(tp->csr6 | 0x0002, ioaddr + CSR6); outl(tp->csr6 | 0x2002, ioaddr + CSR6); } @@ -2297,6 +2687,8 @@ /* Log errors. */ if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 == 0xffffffff) + break; if (csr5 & TxJabber) tp->stats.tx_errors++; if (csr5 & TxFIFOUnderflow) { if ((tp->csr6 & 0xC000) != 0xC000) @@ -2310,32 +2702,30 @@ if (csr5 & RxDied) { /* Missed a Rx frame. */ tp->stats.rx_errors++; tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + outl(tp->csr6 | 0x2002, ioaddr + CSR6); } 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", + if (tulip_debug > 2) + printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", dev->name, csr5); - t21142_lnk_change(dev); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { + if (tp->link_change) + (tp->link_change)(dev, csr5); } /* Clear all error sources, included undocumented ones! */ outl(0x0800f7ba, ioaddr + CSR5); } if (--work_budget < 0) { if (tulip_debug > 1) - printk(KERN_WARNING "%s: Too much work at interrupt, " + printk(KERN_WARNING "%s: Too much work during an interrupt, " "csr5=0x%8.8x.\n", dev->name, csr5); /* Acknowledge all interrupt sources. */ outl(0x8001ffff, ioaddr + CSR5); -#ifdef notdef - /* Clear all but standard interrupt sources. */ - outl((~csr5) & 0x0001ebef, ioaddr + CSR7); -#endif + /* Clear all interrupting sources, set timer to re-enable. */ + outl(((~csr5) & 0x0001ebef) | 0x0800, ioaddr + CSR7); + outl(12, ioaddr + CSR11); break; } } while (1); @@ -2344,8 +2734,11 @@ printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); +#if defined(__i386__) + clear_bit(0, (void*)&dev->interrupt); +#else dev->interrupt = 0; - clear_bit(0, (void*)&tp->interrupt); +#endif return; } @@ -2364,70 +2757,72 @@ while (tp->rx_ring[entry].status >= 0) { s32 status = tp->rx_ring[entry].status; + if (tulip_debug > 5) + printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, + tp->rx_ring[entry].status); if (--rx_work_limit < 0) break; - if ((status & 0x0300) != 0x0300) { - if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %8.8x!\n", + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame " + "spanned multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; + } + } else if (status & RxDescFatalErr) { + /* There was a fatal error. */ + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", dev->name, status); - tp->stats.rx_length_errors++; + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; } - } else if (status & 0x8000) { - /* There was a fatal error. */ - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", - dev->name, status); - tp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) tp->stats.rx_length_errors++; - if (status & 0x0004) tp->stats.rx_frame_errors++; - if (status & 0x0002) tp->stats.rx_crc_errors++; - if (status & 0x0001) tp->stats.rx_fifo_errors++; } else { /* Omit the four octet CRC from the length. */ - short pkt_len = (status >> 16) - 4; + short pkt_len = ((status >> 16) & 0x7ff) - 4; struct sk_buff *skb; - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ +#ifndef final_version + if (pkt_len > 1518) { + printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", + dev->name, pkt_len, pkt_len); + pkt_len = 1518; + tp->stats.rx_length_errors++; + } +#endif + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak - && (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) { + && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ -#if LINUX_VERSION_CODE < 0x10300 - memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len); -#elif LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); -#else +#if ! defined(__alpha__) eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len, 0); skb_put(skb, pkt_len); +#else + memcpy(skb_put(skb, pkt_len), + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); #endif work_done++; } else { /* Pass up the skb already on the Rx ring. */ - skb = tp->rx_skbuff[entry]; + char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); tp->rx_skbuff[entry] = NULL; #ifndef final_version - { - void *temp = skb_put(skb, pkt_len); - if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) - printk(KERN_ERR "%s: Internal consistency error! The " - "skbuff addresses do not match in tulip_rx:" - " %p vs. %p / %p.\n", dev->name, - bus_to_virt(tp->rx_ring[entry].buffer1), - skb->head, temp); - } -#else - skb_put(skb, pkt_len); + if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal fault: The skbuff addresses " + "do not match in tulip_rx: %p vs. %p / %p.\n", + dev->name, bus_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); #endif } -#if LINUX_VERSION_CODE > 0x10300 skb->protocol = eth_type_trans(skb, dev); -#else - skb->len = pkt_len; -#endif netif_rx(skb); dev->last_rx = jiffies; tp->stats.rx_packets++; @@ -2443,18 +2838,14 @@ entry = tp->dirty_rx % RX_RING_SIZE; if (tp->rx_skbuff[entry] == NULL) { struct sk_buff *skb; - skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ); + skb = tp->rx_skbuff[entry] = dev_alloc_skb(PKT_BUF_SZ); if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ -#if LINUX_VERSION_CODE > 0x10300 tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); -#else - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data); -#endif work_done++; } - tp->rx_ring[entry].status = 0x80000000; + tp->rx_ring[entry].status = DescOwned; } return work_done; @@ -2482,16 +2873,14 @@ if (tp->chip_id == DC21040) outl(0x00000004, ioaddr + CSR13); - tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + if (inl(ioaddr + CSR6) != 0xffffffff) + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; del_timer(&tp->timer); -#ifdef SA_SHIRQ free_irq(dev->irq, dev); -#else - free_irq(dev->irq); - irq2dev_map[dev->irq] = 0; -#endif + + dev->if_port = tp->saved_if_port; /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -2504,31 +2893,26 @@ #if LINUX_VERSION_CODE < 0x20100 skb->free = 1; #endif -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(skb); -#else - dev_kfree_skb(skb, FREE_WRITE); -#endif + dev_free_skb(skb); } } for (i = 0; i < TX_RING_SIZE; i++) { if (tp->tx_skbuff[i]) -#if (LINUX_VERSION_CODE > 0x20155) - dev_kfree_skb(tp->tx_skbuff[i]); -#else - dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); -#endif + dev_free_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } + /* Leave the driver in snooze, not sleep, mode. */ + if (tp->flags & HAS_PWRDWN) + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, + 0x40000000); MOD_DEC_USE_COUNT; return 0; } -static struct enet_statistics * -tulip_get_stats(struct device *dev) +static struct net_device_stats *tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; @@ -2551,31 +2935,33 @@ switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ - if (tp->mtable && tp->mtable->has_mii) + if (tp->mii_cnt) data[0] = phy; - else if (tp->chip_id == DC21142) + else if (tp->flags & HAS_NWAY143) data[0] = 32; + else if (tp->chip_id == COMET) + data[0] = 1; else return -ENODEV; return 0; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ - if (data[0] == 32) { /* 21142 pseudo-MII */ + if (data[0] == 32 && (tp->flags & HAS_NWAY143)) { int csr12 = inl(ioaddr + CSR12); int csr14 = inl(ioaddr + CSR14); switch (data[1]) { case 0: { - data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000); + data[3] = (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; + data[3] = ((csr14>>9)&0x07C0) + + ((inl(ioaddr + CSR6)>>3)&0x0040) + ((csr14>>1)&0x20) + 1; break; } - case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + case 5: data[3] = csr12 >> 16; break; default: data[3] = 0; break; } } else { @@ -2586,9 +2972,11 @@ } return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ - if (!suser()) + if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (data[0] == 32) { /* 21142 pseudo-MII */ + if (data[0] == 32 && (tp->flags & HAS_NWAY143)) { + if (data[1] == 5) + tp->to_advertise = data[2]; } else { save_flags(flags); cli(); @@ -2613,9 +3001,9 @@ 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) +static inline u32 ether_crc_le(int length, unsigned char *data) { - unsigned int crc = 0xffffffff; /* Initial value. */ + u32 crc = 0xffffffff; /* Initial value. */ while(--length >= 0) { unsigned char current_octet = *data++; int bit; @@ -2629,12 +3017,22 @@ } return crc; } +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ + int crc = -1; + + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 0; bit < 8; bit++, current_octet >>= 1) + crc = (crc << 1) ^ + ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); + } + return crc; +} -#ifdef NEW_MULTICAST static void set_rx_mode(struct device *dev) -#else -static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) -#endif { long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; @@ -2642,67 +3040,84 @@ tp->csr6 &= ~0x00D5; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - outl(csr6 | 0x00C0, ioaddr + CSR6); + tp->csr6 |= 0x00C0; + csr6 |= 0x00C0; /* Unconditionally log net taps. */ printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); - tp->csr6 |= 0xC0; } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - outl(csr6 | 0x0080, ioaddr + CSR6); - tp->csr6 |= 0x80; + /* Too many to filter well -- accept all multicasts. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else if (tp->flags & MC_HASH_ONLY) { + /* Some work-alikes have only a 64-entry hash filter table. */ + /* Should verify correctness on big-endian/__powerpc__ */ + struct dev_mc_list *mclist; + int i; + u32 mc_filter[2]; /* Multicast hash filter */ + if (dev->mc_count > 64) { /* Arbitrary non-effective limit. */ + tp->csr6 |= 0x0080; + csr6 |= 0x0080; + } else { + mc_filter[1] = mc_filter[0] = 0; + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter); + if (tp->chip_id == AX88140) { + outl(2, ioaddr + CSR13); + outl(mc_filter[0], ioaddr + CSR14); + outl(3, ioaddr + CSR13); + outl(mc_filter[1], ioaddr + CSR14); + } else if (tp->chip_id == COMET) { /* Has a simple hash filter. */ + outl(mc_filter[0], ioaddr + 0xAC); + outl(mc_filter[1], ioaddr + 0xB0); + } + } } else { - u32 *setup_frm = tp->setup_frame; + u16 *eaddrs, *setup_frm = tp->setup_frame; struct dev_mc_list *mclist; - u16 *eaddrs; - u32 tx_flags; + u32 tx_flags = 0x08000000 | 192; int i; + /* Note that only the low-address shortword of setup_frame is valid! + The values are doubled for big-endian architectures. */ if (dev->mc_count > 14) { /* Must use a multicast hash table. */ - u16 hash_table[32]; - memset(hash_table, 0, sizeof(hash_table)); - /* This should work on big-endian machines as well. */ - for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; - i++, mclist = mclist->next) - set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, - hash_table); - /* Copy the hash table to the setup frame. - NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */ - for (i = 0; i < 32; i++) - *setup_frm++ = hash_table[i]; - setup_frm += 7; - tx_flags = 0x08400000 | 192; - /* Too clever: i > 15 for fall-though. */ + u16 hash_table[32]; + tx_flags = 0x08400000 | 192; /* Use hash filter. */ + memset(hash_table, 0, sizeof(hash_table)); + set_bit(255, hash_table); /* Broadcast entry */ + /* 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); + for (i = 0; i < 32; i++) + *setup_frm++ = *setup_frm++ = hash_table[i]; + setup_frm = &tp->setup_frame[13*6]; } else { - /* We have <= 15 addresses so we can use the wonderful - 16 address perfect filtering of the Tulip. */ - for (i = 0, mclist = dev->mc_list; i < dev->mc_count; - i++, mclist = mclist->next) { - /* Note that only the low shortword of setup_frame[] is valid! - This code may require tweaking for non-x86 architectures! */ - eaddrs = (u16 *)mclist->dmi_addr; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - *setup_frm++ = *eaddrs++; - } - /* Fill the rest of the table with our physical address. - Once again, only the low shortword or setup_frame[] is valid! */ - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - *setup_frm++ = 0xffff; - tx_flags = 0x08000000 | 192; + /* We have <= 14 addresses so we can use the wonderful + 16 address perfect filtering of the Tulip. */ + for (i = 0, mclist = dev->mc_list; i < dev->mc_count; + i++, mclist = mclist->next) { + eaddrs = (u16 *)mclist->dmi_addr; + *setup_frm++ = *setup_frm++ = *eaddrs++; + *setup_frm++ = *setup_frm++ = *eaddrs++; + *setup_frm++ = *setup_frm++ = *eaddrs++; + } + /* Fill the unused entries with the broadcast address. */ + memset(setup_frm, 0xff, (15-i)*12); + setup_frm = &tp->setup_frame[15*6]; } + /* Fill the final entry with our physical address. */ eaddrs = (u16 *)dev->dev_addr; - do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; - } while (++i < 15); + *setup_frm++ = *setup_frm++ = eaddrs[0]; + *setup_frm++ = *setup_frm++ = eaddrs[1]; + *setup_frm++ = *setup_frm++ = eaddrs[2]; /* Now add this frame to the Tx list. */ if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { /* Same setup recently queued, we need not add it. */ } else { unsigned long flags; - unsigned int entry, dummy = 0; + unsigned int entry; save_flags(flags); cli(); entry = tp->cur_tx++ % TX_RING_SIZE; @@ -2711,32 +3126,29 @@ /* Avoid a chip errata by prefixing a dummy entry. */ tp->tx_skbuff[entry] = 0; tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0; tp->tx_ring[entry].buffer1 = 0; - /* race with chip, set DescOwned later */ - dummy = entry; + tp->tx_ring[entry].status = DescOwned; 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 |= DESC_RING_WRAP; /* Wrap ring. */ tp->tx_ring[entry].length = tx_flags; tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = 0x80000000; + tp->tx_ring[entry].status = DescOwned; if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { - dev->tbusy = 1; + set_bit(0, (void*)&dev->tbusy); tp->tx_full = 1; } - if (dummy >= 0) - tp->tx_ring[dummy].status = DescOwned; restore_flags(flags); /* Trigger an immediate transmit demand. */ outl(0, ioaddr + CSR1); } - outl(csr6 | 0x0000, ioaddr + CSR6); } + outl(csr6 | 0x0000, ioaddr + CSR6); } #ifdef CARDBUS @@ -2745,18 +3157,18 @@ static dev_node_t *tulip_attach(dev_locator_t *loc) { + struct device *dev; u16 dev_id; u32 io; - u8 bus, devfn; - struct device *dev; + u8 bus, devfn, irq; if (loc->bus != LOC_PCI) return NULL; bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); - io &= ~3; - dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); + pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); + dev = tulip_probe1(bus, devfn, NULL, io & ~3, irq, DC21142, 0); if (dev) { dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); strcpy(node->dev_name, dev->name); @@ -2768,6 +3180,45 @@ return NULL; } +static void tulip_suspend(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + long ioaddr = (*devp)->base_addr; + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + int csr6 = inl(ioaddr + CSR6); + /* Disable interrupts, stop the chip, gather stats. */ + if (csr6 != 0xffffffff) { + outl(0x00000000, ioaddr + CSR7); + outl(csr6 & ~0x2002, ioaddr + CSR6); + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + } + tulip_close(*devp); + /* Put the 21143 into sleep mode. */ + pcibios_write_config_dword(tp->pci_bus,tp->pci_devfn, 0x40,0x80000000); + } +} + +static void tulip_resume(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; + pcibios_write_config_dword(tp->pci_bus, tp->pci_devfn, 0x40, 0x0000); + tulip_open(*devp); + } +} + static void tulip_detach(dev_node_t *node) { struct device **devp, **next; @@ -2786,34 +3237,17 @@ } struct driver_operations tulip_ops = { - "tulip_cb", tulip_attach, NULL, NULL, tulip_detach + "tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach }; #endif /* Cardbus support */ #ifdef MODULE -#if LINUX_VERSION_CODE > 0x20118 -MODULE_AUTHOR("Donald Becker "); -MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); -MODULE_PARM(debug, "i"); -MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM(reverse_probe, "i"); -MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); -#endif - -/* An additional parameter that may be passed in... */ -static int debug = -1; - -int -init_module(void) +int init_module(void) { - if (debug >= 0) - tulip_debug = debug; - #ifdef CARDBUS + reverse_probe = 0; /* Not used. */ register_driver(&tulip_ops); return 0; #else @@ -2821,8 +3255,7 @@ #endif } -void -cleanup_module(void) +void cleanup_module(void) { struct device *next_dev; @@ -2832,9 +3265,11 @@ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { - next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; + struct tulip_private *tp = (struct tulip_private *)root_tulip_dev->priv; + next_dev = tp->next_module; unregister_netdev(root_tulip_dev); - release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE); + release_region(root_tulip_dev->base_addr, + tulip_tbl[tp->chip_id].io_size); kfree(root_tulip_dev); root_tulip_dev = next_dev; } @@ -2844,8 +3279,9 @@ /* * Local variables: - * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" - * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * cardbus-compile-command: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia-cs-3.0.9/include/" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/pci/oldproc.c linux.14p2/drivers/pci/oldproc.c --- linux.vanilla/drivers/pci/oldproc.c Wed Oct 20 01:12:39 1999 +++ linux.14p2/drivers/pci/oldproc.c Fri Oct 22 21:24:50 1999 @@ -526,6 +526,7 @@ DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82380FB, "82380FB Mobile"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), + DEVICE( INTEL, INTEL_MEGARAID, "OEM MegaRAID Controller"), DEVICE( INTEL, INTEL_82371SB_0,"82371SB PIIX3 ISA"), DEVICE( INTEL, INTEL_82371SB_1,"82371SB PIIX3 IDE"), DEVICE( INTEL, INTEL_82371SB_2,"82371SB PIIX3 USB"), @@ -536,13 +537,16 @@ 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( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), 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( INTEL, INTEL_82450GX, "450KX/GX [Orion] - 82454KX/GX PCI Bridge"), + DEVICE( INTEL, INTEL_82453GX, "450KX/GX [Orion] - 82453KX/GX Memory Controller"), + DEVICE( INTEL, INTEL_82451NX, "450NX - 82451NX Memory & I/O Controller"), + DEVICE( INTEL, INTEL_82454NX, "450NX - 82454NX PCI Expander Bridge"), + DEVICE( COMPUTONE, COMPUTONE_IP2EX, "Computone IntelliPort Plus"), DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7810, "AIC-7810 RAID"), DEVICE( ADAPTEC, ADAPTEC_7821, "AIC-7860"), diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/fdomain.c linux.14p2/drivers/scsi/fdomain.c --- linux.vanilla/drivers/scsi/fdomain.c Tue Dec 29 19:44:53 1998 +++ linux.14p2/drivers/scsi/fdomain.c Fri Oct 22 22:32:09 1999 @@ -875,6 +875,8 @@ int retcode; struct Scsi_Host *shpnt; #if DO_DETECT + int i = 0; + int j = 0; const int buflen = 255; Scsi_Cmnd SCinit; unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 }; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/gdth.h linux.14p2/drivers/scsi/gdth.h --- linux.vanilla/drivers/scsi/gdth.h Sat Aug 14 02:25:41 1999 +++ linux.14p2/drivers/scsi/gdth.h Fri Oct 22 23:26:35 1999 @@ -249,8 +249,10 @@ #define HIGH_PRI 0x08 /* data directions */ +#ifndef HOSTS_C #define DATA_IN 0x01000000L /* data from target */ #define DATA_OUT 0x00000000L /* data to target */ +#endif /* BMIC registers (EISA controllers) */ #define ID0REG 0x0c80 /* board ID */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/ide-scsi.c linux.14p2/drivers/scsi/ide-scsi.c --- linux.vanilla/drivers/scsi/ide-scsi.c Sat Aug 14 02:27:31 1999 +++ linux.14p2/drivers/scsi/ide-scsi.c Wed Oct 27 18:56:41 1999 @@ -308,7 +308,7 @@ /* * Our interrupt handler. */ -static void idescsi_pc_intr (ide_drive_t *drive) +static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) { idescsi_scsi_t *scsi = drive->driver_data; byte status, ireason; @@ -338,15 +338,14 @@ if (status & ERR_STAT) rq->errors++; idescsi_end_request (1, HWGROUP(drive)); - return; + return ide_stopped; } bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG); ireason = IN_BYTE (IDE_IREASON_REG); if (ireason & IDESCSI_IREASON_COD) { printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } if (ireason & IDESCSI_IREASON_IO) { temp = pc->actually_transferred + bcount; @@ -366,7 +365,7 @@ pc->current_position += temp; idescsi_discard_data (drive,bcount - temp); ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); - return; + return ide_started; } #if IDESCSI_DEBUG_LOG printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n"); @@ -390,32 +389,34 @@ pc->current_position+=bcount; ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* And set the interrupt handler again */ + return ide_started; } -static void idescsi_transfer_pc (ide_drive_t *drive) +static ide_startstop_t idescsi_transfer_pc (ide_drive_t *drive) { idescsi_scsi_t *scsi = drive->driver_data; idescsi_pc_t *pc = scsi->pc; byte ireason; + ide_startstop_t startstop; - if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n"); - return; + return startstop; } ireason = IN_BYTE (IDE_IREASON_REG); if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) { printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n"); - ide_do_reset (drive); - return; + return ide_do_reset (drive); } ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc)); /* Set the interrupt routine */ atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */ + return ide_started; } /* * Issue a packet command */ -static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) +static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) { idescsi_scsi_t *scsi = drive->driver_data; int bcount; @@ -443,16 +444,17 @@ if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc)); OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; } else { OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); - idescsi_transfer_pc (drive); + return idescsi_transfer_pc (drive); } } /* * idescsi_do_request is our request handling function. */ -static void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) { #if IDESCSI_DEBUG_LOG printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors); @@ -460,11 +462,11 @@ #endif /* IDESCSI_DEBUG_LOG */ if (rq->cmd == IDESCSI_PC_RQ) { - idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer); - return; + return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer); } printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd); idescsi_end_request (0,HWGROUP (drive)); + return ide_stopped; } static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/ips.h linux.14p2/drivers/scsi/ips.h --- linux.vanilla/drivers/scsi/ips.h Wed Oct 20 01:12:41 1999 +++ linux.14p2/drivers/scsi/ips.h Wed Oct 27 19:24:55 1999 @@ -233,6 +233,7 @@ /* * DCDB Table Equates */ +#ifndef HOSTS_C #define NO_DISCONNECT 0x00 #define DISCONNECT_ALLOWED 0x80 #define NO_AUTO_REQUEST_SENSE 0x40 @@ -243,7 +244,6 @@ #define TIMEOUT10 0x10 #define TIMEOUT60 0x20 #define TIMEOUT20M 0x30 - /* * Host adapter Flags (bit numbers) */ @@ -256,7 +256,7 @@ */ #define SCB_ACTIVE 0x00001 #define SCB_WAITING 0x00002 - +#endif /* HOSTS_C */ /* * Passthru stuff */ @@ -590,6 +590,8 @@ /* * Inquiry Data Format */ +#ifndef HOSTS_C + typedef struct { u8 DeviceType:5; u8 DeviceTypeQualifier:3; @@ -614,6 +616,7 @@ u8 Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif /* * Read Capacity Data Format */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/megaraid.h linux.14p2/drivers/scsi/megaraid.h --- linux.vanilla/drivers/scsi/megaraid.h Sat Aug 28 20:00:57 1999 +++ linux.14p2/drivers/scsi/megaraid.h Fri Oct 22 23:26:35 1999 @@ -11,6 +11,7 @@ #define BOARD_QUARTZ 0x08000000L #define BOARD_40LD 0x04000000L +#ifndef HOSTS_C #define SCB_FREE 0x0 #define SCB_ACTIVE 0x1 #define SCB_WAITQ 0x2 @@ -18,6 +19,7 @@ #define SCB_COMPLETE 0x4 #define SCB_ABORTED 0x5 #define SCB_RESET 0x6 +#endif #define MEGA_CMD_TIMEOUT 10 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/psi240i.h linux.14p2/drivers/scsi/psi240i.h --- linux.vanilla/drivers/scsi/psi240i.h Mon Dec 28 06:19:22 1998 +++ linux.14p2/drivers/scsi/psi240i.h Fri Oct 22 23:26:35 1999 @@ -211,6 +211,8 @@ } READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; // SCSI inquiry data +#ifndef HOSTS_C + typedef struct _INQUIRYDATA { UCHAR DeviceType :5; @@ -235,6 +237,7 @@ UCHAR VendorSpecific[20]; UCHAR Reserved3[40]; } INQUIRYDATA, *PINQUIRYDATA; +#endif // IDE IDENTIFY data typedef struct _IDENTIFY_DATA diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/qlogicfc.c linux.14p2/drivers/scsi/qlogicfc.c --- linux.vanilla/drivers/scsi/qlogicfc.c Wed Oct 20 01:12:41 1999 +++ linux.14p2/drivers/scsi/qlogicfc.c Fri Oct 22 21:25:11 1999 @@ -1855,7 +1855,7 @@ static int isp2x00_init(struct Scsi_Host *sh) { - u_int io_base; + u_long io_base; struct isp2x00_hostdata *hostdata; u_char revision; u_int irq; diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/qlogicisp.c linux.14p2/drivers/scsi/qlogicisp.c --- linux.vanilla/drivers/scsi/qlogicisp.c Wed Oct 20 01:12:42 1999 +++ linux.14p2/drivers/scsi/qlogicisp.c Fri Oct 22 21:25:11 1999 @@ -1262,6 +1262,13 @@ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, latency_timer); } #endif +#ifdef __alpha__ + /* Force ALPHA to use bus I/O and not bus MEM. + This is to avoid having to use HAE_MEM registers, + which is broken on some platforms and with SMP. + */ + command &= ~PCI_COMMAND_MEMORY; +#endif if ((command & PCI_COMMAND_MEMORY) && ((mem_base & 1) == 0)) { diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/sym53c416.h linux.14p2/drivers/scsi/sym53c416.h --- linux.vanilla/drivers/scsi/sym53c416.h Sun Mar 28 19:04:11 1999 +++ linux.14p2/drivers/scsi/sym53c416.h Fri Oct 22 23:26:35 1999 @@ -22,7 +22,9 @@ #include #endif +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #include #include diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/sym53c8xx_defs.h linux.14p2/drivers/scsi/sym53c8xx_defs.h --- linux.vanilla/drivers/scsi/sym53c8xx_defs.h Wed Oct 20 01:12:47 1999 +++ linux.14p2/drivers/scsi/sym53c8xx_defs.h Fri Oct 22 23:26:35 1999 @@ -66,8 +66,9 @@ #endif #include +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) - +#endif /* * NCR PQS/PDS special device support. */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/scsi/u14-34f.h linux.14p2/drivers/scsi/u14-34f.h --- linux.vanilla/drivers/scsi/u14-34f.h Sun Mar 28 19:04:08 1999 +++ linux.14p2/drivers/scsi/u14-34f.h Fri Oct 22 23:26:35 1999 @@ -18,7 +18,9 @@ #define U14_34F_VERSION "4.33.00" +#ifndef LinuxVersionCode #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) +#endif #if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101) diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/Config.in linux.14p2/drivers/sound/Config.in --- linux.vanilla/drivers/sound/Config.in Wed Oct 20 01:12:47 1999 +++ linux.14p2/drivers/sound/Config.in Fri Oct 22 22:52:53 1999 @@ -115,8 +115,6 @@ int 'SB MPU401 IRQ (Jazz16, SM Wave and ES1688) Check from manual of the card' CONFIG_SB_MPU_IRQ -1 fi - dep_tristate 'Generic OPL2/OPL3 FM synthesizer support' CONFIG_SOUND_ADLIB $CONFIG_SOUND_OSS - dep_tristate 'Gravis Ultrasound support' CONFIG_SOUND_GUS $CONFIG_SOUND_OSS if [ "$CONFIG_SOUND_GUS" = "y" -o "$CONFIG_SOUND_GUS" = "m" ]; then bool '16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_GUS16 diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/Makefile linux.14p2/drivers/sound/Makefile --- linux.vanilla/drivers/sound/Makefile Wed Oct 20 01:12:47 1999 +++ linux.14p2/drivers/sound/Makefile Fri Oct 22 22:52:53 1999 @@ -43,7 +43,6 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += dmasound.o obj-$(CONFIG_SOUND_OSS) += sound.o -obj-$(CONFIG_SOUND_ADLIB) += adlib_card.o opl3.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o # In theory, there's probably no reason to include the uart401 code diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/ac97.h linux.14p2/drivers/sound/ac97.h --- linux.vanilla/drivers/sound/ac97.h Wed Oct 20 01:12:47 1999 +++ linux.14p2/drivers/sound/ac97.h Wed Oct 27 19:38:39 1999 @@ -33,6 +33,19 @@ /* registers 0x0028 - 0x0058 are reserved */ +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ +/* range 0x3c-0x58 - MODEM */ + /* registers 0x005a - 0x007a are vendor reserved */ #define AC97_VENDOR_ID1 0x007c diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/adlib_card.c linux.14p2/drivers/sound/adlib_card.c --- linux.vanilla/drivers/sound/adlib_card.c Thu May 14 18:33:17 1998 +++ linux.14p2/drivers/sound/adlib_card.c Fri Oct 22 22:52:53 1999 @@ -16,8 +16,6 @@ #include "sound_config.h" #include "soundmodule.h" -#ifdef CONFIG_YM3812 - void attach_adlib_card(struct address_info *hw_config) { hw_config->slots[0] = opl3_init(hw_config->io_base, hw_config->osp); @@ -69,5 +67,4 @@ SOUND_LOCK_END; } -#endif #endif diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/es1370.c linux.14p2/drivers/sound/es1370.c --- linux.vanilla/drivers/sound/es1370.c Sat Aug 14 02:27:34 1999 +++ linux.14p2/drivers/sound/es1370.c Fri Oct 22 22:34:17 1999 @@ -101,6 +101,17 @@ * 15.06.99 0.23 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.24 Add pci_set_master + * 02.08.99 0.25 Added workaround for the "phantom write" bug first + * documented by Dave Sharpless from Anchor Games + * 03.08.99 0.26 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1370=joystick[,lineout[,micbias]]" + * removed CONFIG_SOUND_ES1370_JOYPORT_BOOT kludge + * 12.08.99 0.27 module_init/__setup fixes + * 19.08.99 0.28 SOUND_MIXER_IMIX fixes, reported by Gianluca + * 31.08.99 0.29 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * 03.09.99 0.30 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race * * some important things missing in Ensoniq documentation: * @@ -123,7 +134,6 @@ /*****************************************************************************/ -#include #include #include #include @@ -175,12 +185,14 @@ #define ES1370_REG_DAC2_SCOUNT 0x28 #define ES1370_REG_ADC_SCOUNT 0x2c -#define ES1370_REG_DAC1_FRAMEADR 0xc30 -#define ES1370_REG_DAC1_FRAMECNT 0xc34 -#define ES1370_REG_DAC2_FRAMEADR 0xc38 -#define ES1370_REG_DAC2_FRAMECNT 0xc3c -#define ES1370_REG_ADC_FRAMEADR 0xd30 -#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 +#define ES1370_REG_PHANTOM_FRAMEADR 0xd38 +#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c #define ES1370_FMT_U8_MONO 0 #define ES1370_FMT_U8_STEREO 1 @@ -360,6 +372,13 @@ static struct es1370_state *devs = NULL; +/* + * The following buffer is used to point the phantom write channel to, + * so that it cannot wreak havoc. The attribute makes sure it doesn't + * cross a page boundary and ensures dword alignment for the DMA engine + */ +static unsigned char bugbuf[16] __attribute__ ((aligned (16))); + /* --------------------------------------------------------------------- */ extern inline unsigned ld2(unsigned int x) @@ -779,10 +798,36 @@ [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } /* mono out */ }; +static void set_recsrc(struct es1370_state *s, unsigned int val) +{ + unsigned int i, j; + + for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(val & (1 << i))) + continue; + if (!mixtable[i].recmask) { + val &= ~(1 << i); + continue; + } + j |= mixtable[i].recmask; + } + s->mix.recsrc = val; + wrcodec(s, 0x12, j & 0xd5); + wrcodec(s, 0x13, j & 0xaa); + wrcodec(s, 0x14, (j >> 8) & 0x17); + wrcodec(s, 0x15, (j >> 8) & 0x0f); + i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; + if (!s->mix.imix) { + i &= 0xff60; /* mute record and line monitor */ + } + wrcodec(s, 0x10, i); + wrcodec(s, 0x11, i >> 8); +} + static int mixer_ioctl(struct es1370_state *s, unsigned int cmd, unsigned long arg) { unsigned long flags; - int i, val, j; + int i, val; unsigned char l, r, rl, rr; VALIDATE_STATE(s); @@ -887,34 +932,13 @@ switch (_IOC_NR(cmd)) { case SOUND_MIXER_IMIX: - if (arg == 0) - return -EFAULT; - get_user_ret(s->mix.imix,(int *)arg, -EFAULT); - val = s->mix.recsrc; - /* fall through */ + get_user_ret(s->mix.imix, (int *)arg, -EFAULT); + set_recsrc(s, s->mix.recsrc); + return 0; case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ get_user_ret(val, (int *)arg, -EFAULT); - for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!(val & (1 << i))) - continue; - if (!mixtable[i].recmask) { - val &= ~(1 << i); - continue; - } - j |= mixtable[i].recmask; - } - s->mix.recsrc = val; - wrcodec(s, 0x12, j & 0xd5); - wrcodec(s, 0x13, j & 0xaa); - wrcodec(s, 0x14, (j >> 8) & 0x17); - wrcodec(s, 0x15, (j >> 8) & 0x0f); - i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60; - if (!s->mix.imix) { - i &= 0xff60; /* mute record and line monitor */ - } - wrcodec(s, 0x10, i); - wrcodec(s, 0x11, i >> 8); + set_recsrc(s, val); return 0; default: @@ -1033,7 +1057,7 @@ static int drain_dac1(struct es1370_state *s, int nonblock) { - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL }; unsigned long flags; int count, tmo; @@ -1054,9 +1078,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 + / dac1_samplerate[(s->ctrl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL]; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac1.wait, &wait); @@ -1089,9 +1114,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) DBG(printk(KERN_DEBUG "es1370: dma timed out??\n");) } remove_wait_queue(&s->dma_dac2.wait, &wait); @@ -2049,6 +2075,7 @@ static ssize_t es1370_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2059,7 +2086,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -2070,15 +2100,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -2087,13 +2127,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t es1370_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1370_state *s = (struct es1370_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2104,7 +2148,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -2117,15 +2164,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -2138,6 +2195,8 @@ es1370_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2217,7 +2276,7 @@ static int es1370_midi_release(struct inode *inode, struct file *file) { struct es1370_state *s = (struct es1370_state *)file->private_data; - struct wait_queue wait = { current, NULL }; + struct wait_queue wait = { current, NULL }; unsigned long flags; unsigned count, tmo; @@ -2321,7 +2380,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.24 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.30 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2341,6 +2400,7 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = ES1370_MAGIC; s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; @@ -2385,6 +2445,10 @@ /* initialize the chips */ outl(s->ctrl, s->io+ES1370_REG_CONTROL); outl(s->sctrl, s->io+ES1370_REG_SERIAL_CONTROL); + /* point phantom write channel to "bugbuf" */ + outl((ES1370_REG_PHANTOM_FRAMEADR >> 8) & 15, s->io+ES1370_REG_MEMPAGE); + outl(virt_to_bus(bugbuf), s->io+(ES1370_REG_PHANTOM_FRAMEADR & 0xff)); + outl(0, s->io+(ES1370_REG_PHANTOM_FRAMECNT & 0xff)); pci_set_master(pcidev); /* enable bus mastering */ wrcodec(s, 0x16, 3); /* no RST, PD */ wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */ diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/es1371.c linux.14p2/drivers/sound/es1371.c --- linux.vanilla/drivers/sound/es1371.c Sat Aug 14 02:27:34 1999 +++ linux.14p2/drivers/sound/es1371.c Fri Oct 22 22:34:18 1999 @@ -3,7 +3,7 @@ /* * es1371.c -- Creative Ensoniq ES1371. * - * Copyright (C) 1998 Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,14 +68,31 @@ * 15.06.99 0.12 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.13 Add pci_set_master - * 21.07.99 0.14 S/PDIF module option for cards revision >= 4. Initial version - * by Dave Platt . + * 03.08.99 0.14 adapt to Linus' new __setup/__initcall + * added kernel command line option "es1371=joystickaddr" + * removed CONFIG_SOUND_ES1371_JOYPORT_BOOT kludge + * 10.08.99 0.15 (Re)added S/PDIF module option for cards revision >= 4. + * Initial version by Dave Platt . + * module_init/__setup fixes + * 08.16.99 0.16 Joe Cotellese + * Added detection for ES1371 revision ID so that we can + * detect the ES1373 and later parts. + * added AC97 #defines for readability + * added a /proc file system for dumping hardware state + * updated SRC and CODEC w/r functions to accomodate bugs + * in some versions of the ES137x chips. + * 31.08.99 0.17 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * replaced current->state = x with set_current_state(x) + * 03.09.99 0.18 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 21.10.99 0.19 Round sampling rates, requested by + * Kasamatsu Kenichi * */ /*****************************************************************************/ -#include #include #include #include @@ -86,17 +103,21 @@ #include #include #include -#include -#include #include #include +#include +#include #include +#include +#include #include #include +#include "ac97.h" /* --------------------------------------------------------------------- */ #undef OSS_DOCUMENTED_MIXER_SEMANTICS +#undef ES1371_DEBUG /* --------------------------------------------------------------------- */ @@ -107,6 +128,18 @@ #define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 #endif +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define ES1371REV_ES1371_B 0x09 + + #define ES1371_MAGIC ((PCI_VENDOR_ID_ENSONIQ<<16)|PCI_DEVICE_ID_ENSONIQ_ES1371) #define ES1371_EXTENT 0x40 @@ -206,14 +239,22 @@ #define UCTRL_CNTRL_SWR 0x03 /* software reset command */ /* sample rate converter */ +#define SRC_OKSTATE 1 + #define SRC_RAMADDR_MASK 0xfe000000 #define SRC_RAMADDR_SHIFT 25 +#define SRC_DAC1FREEZE (1UL << 21) +#define SRC_DAC2FREEZE (1UL << 20) +#define SRC_ADCFREEZE (1UL << 19) + + #define SRC_WE 0x01000000 /* read/write control for SRC RAM */ #define SRC_BUSY 0x00800000 /* SRC busy */ #define SRC_DIS 0x00400000 /* 1 = disable SRC */ #define SRC_DDAC1 0x00200000 /* 1 = disable accum update for DAC1 */ #define SRC_DDAC2 0x00100000 /* 1 = disable accum update for DAC2 */ #define SRC_DADC 0x00080000 /* 1 = disable accum update for ADC2 */ +#define SRC_CTLMASK 0x00780000 #define SRC_RAMDATA_MASK 0x0000ffff #define SRC_RAMDATA_SHIFT 0 @@ -293,7 +334,7 @@ /* misc stuff */ - +#define POLL_COUNT 0x1000 #define FMODE_DAC 4 /* slight misuse of mode_t */ /* MIDI buffer sizes */ @@ -352,8 +393,13 @@ /* hardware resources */ unsigned long io; /* long for SPARC */ unsigned int irq; - - /* mixer registers; there is no HW readback */ + u8 rev; /* the chip revision */ + +#ifdef ES1371_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; +#endif /* ES1371_DEBUG */ + /* mixer registers; there is no HW readback */ struct { unsigned short codec_id; unsigned int modcnt; @@ -438,31 +484,12 @@ } /* --------------------------------------------------------------------- */ -/* - * hweightN: returns the hamming weight (i.e. the number - * of bits set) of a N-bit word - */ - -#ifdef hweight32 -#undef hweight32 -#endif - -extern __inline__ unsigned int hweight32(unsigned int w) -{ - unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); - res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); - return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); -} - -/* --------------------------------------------------------------------- */ static unsigned wait_src_ready(struct es1371_state *s) { unsigned int t, r; - for (t = 0; t < 1000; t++) { + for (t = 0; t < POLL_COUNT; t++) { if (!((r = inl(s->io + ES1371_REG_SRCONV)) & SRC_BUSY)) return r; udelay(1); @@ -473,29 +500,53 @@ static unsigned src_read(struct es1371_state *s, unsigned reg) { - unsigned int r; + unsigned int temp,i,orig; - r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); - r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; - outl(r, s->io + ES1371_REG_SRCONV); - return (wait_src_ready(s) & SRC_RAMDATA_MASK) >> SRC_RAMDATA_SHIFT; -} + /* wait for ready */ + temp = wait_src_ready (s); + + /* we can only access the SRC at certain times, make sure + we're allowed to before we read */ + + orig = temp; + /* expose the SRC state bits */ + outl ( (temp & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT) | 0x10000UL, + s->io + ES1371_REG_SRCONV); + + /* now, wait for busy and the correct time to read */ + temp = wait_src_ready (s); + + if ( (temp & 0x00870000UL ) != ( SRC_OKSTATE << 16 )){ + /* wait for the right state */ + for (i=0; iio + ES1371_REG_SRCONV); + if ( (temp & 0x00870000UL ) == ( SRC_OKSTATE << 16 )) + break; + } + } + /* hide the state bits */ + outl ((orig & SRC_CTLMASK) | (reg << SRC_RAMADDR_SHIFT), s->io + ES1371_REG_SRCONV); + return temp; + + +} static void src_write(struct es1371_state *s, unsigned reg, unsigned data) { + unsigned int r; r = wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC); r |= (reg << SRC_RAMADDR_SHIFT) & SRC_RAMADDR_MASK; r |= (data << SRC_RAMDATA_SHIFT) & SRC_RAMDATA_MASK; outl(r | SRC_WE, s->io + ES1371_REG_SRCONV); + } /* --------------------------------------------------------------------- */ /* most of the following here is black magic */ - static void set_adc_rate(struct es1371_state *s, unsigned rate) { unsigned long flags; @@ -532,6 +583,7 @@ spin_unlock_irqrestore(&s->lock, flags); } + static void set_dac1_rate(struct es1371_state *s, unsigned rate) { unsigned long flags; @@ -541,8 +593,8 @@ rate = 48000; if (rate < 4000) rate = 4000; - freq = (rate << 15) / 3000; - s->dac1rate = (freq * 3000) >> 15; + freq = ((rate << 15) + 1500) / 3000; + s->dac1rate = (freq * 3000 + 16384) >> 15; spin_lock_irqsave(&s->lock, flags); r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC2 | SRC_DADC)) | SRC_DDAC1; outl(r, s->io + ES1371_REG_SRCONV); @@ -564,8 +616,8 @@ rate = 48000; if (rate < 4000) rate = 4000; - freq = (rate << 15) / 3000; - s->dac2rate = (freq * 3000) >> 15; + freq = ((rate << 15) + 1500) / 3000; + s->dac2rate = (freq * 3000 + 16384) >> 15; spin_lock_irqsave(&s->lock, flags); r = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DADC)) | SRC_DDAC2; outl(r, s->io + ES1371_REG_SRCONV); @@ -580,26 +632,80 @@ /* --------------------------------------------------------------------- */ +static void __init src_init(struct es1371_state *s) +{ + unsigned int i; + + /* before we enable or disable the SRC we need + to wait for it to become ready */ + wait_src_ready(s); + + outl(SRC_DIS, s->io + ES1371_REG_SRCONV); + + for (i = 0; i < 0x80; i++) + src_write(s, i, 0); + + src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); + src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); + src_write(s, SRCREG_VOL_ADC, 1 << 12); + src_write(s, SRCREG_VOL_ADC+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1, 1 << 12); + src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); + src_write(s, SRCREG_VOL_DAC2, 1 << 12); + src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); + set_adc_rate(s, 22050); + set_dac1_rate(s, 22050); + set_dac2_rate(s, 22050); + + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) + */ + wait_src_ready(s); + outl(0, s->io+ES1371_REG_SRCONV); +} + +/* --------------------------------------------------------------------- */ + static void wrcodec(struct es1371_state *s, unsigned addr, unsigned data) { unsigned long flags; unsigned t, x; - - for (t = 0; t < 0x1000; t++) + + for (t = 0; t < POLL_COUNT; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; spin_lock_irqsave(&s->lock, flags); - /* save the current state for later */ - x = inl(s->io+ES1371_REG_SRCONV); - /* enable SRC state data in SRC mux */ - outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, + + /* save the current state for later */ + x = wait_src_ready(s); + + /* enable SRC state data in SRC mux */ + outl(( x & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, s->io+ES1371_REG_SRCONV); - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) - break; + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), s->io+ES1371_REG_CODEC); + /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); @@ -611,28 +717,50 @@ unsigned long flags; unsigned t, x; + /* wait for WIP to go away */ for (t = 0; t < 0x1000; t++) if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) break; spin_lock_irqsave(&s->lock, flags); + /* save the current state for later */ - x = inl(s->io+ES1371_REG_SRCONV); + x = (wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)); + /* enable SRC state data in SRC mux */ - outl((wait_src_ready(s) & (SRC_DIS | SRC_DDAC1 | SRC_DDAC2 | SRC_DADC)) | 0x00010000, - s->io+ES1371_REG_SRCONV); - /* wait for a SAFE time to write addr/data and then do it, dammit */ - for (t = 0; t < 0x1000; t++) - if ((inl(s->io+ES1371_REG_SRCONV) & 0x00070000) == 0x00010000) - break; + outl( x | 0x00010000, + s->io+ES1371_REG_SRCONV); + + /* wait for not busy (state 0) first to avoid + transition states */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0 ) + break; + udelay(1); + } + + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t=0; tio+ES1371_REG_SRCONV) & 0x00870000) ==0x00010000) + break; + udelay(1); + } + outl(((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD, s->io+ES1371_REG_CODEC); /* restore SRC reg */ wait_src_ready(s); outl(x, s->io+ES1371_REG_SRCONV); spin_unlock_irqrestore(&s->lock, flags); - /* now wait for the stinkin' data (RDY) */ + + /* wait for WIP again */ for (t = 0; t < 0x1000; t++) + if (!(inl(s->io+ES1371_REG_CODEC) & CODEC_WIP)) + break; + + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) if ((x = inl(s->io+ES1371_REG_CODEC)) & CODEC_RDY) break; + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } @@ -993,6 +1121,21 @@ /* --------------------------------------------------------------------- */ +/* + * AC97 Mixer Register to Connections mapping of the Concert 97 board + * + * AC97_MASTER_VOL_STEREO Line Out + * AC97_MASTER_VOL_MONO TAD Output + * AC97_PCBEEP_VOL none + * AC97_PHONE_VOL TAD Input (mono) + * AC97_MIC_VOL MIC Input (mono) + * AC97_LINEIN_VOL Line Input (stereo) + * AC97_CD_VOL CD Input (stereo) + * AC97_VIDEO_VOL none + * AC97_AUX_VOL Aux Input (stereo) + * AC97_PCMOUT_VOL Wave Output (stereo) + */ + #define AC97_PESSIMISTIC /* @@ -1018,25 +1161,25 @@ static const unsigned char volreg[SOUND_MIXER_NRDEVICES] = { /* 5 bit stereo */ - [SOUND_MIXER_LINE] = 0x10, - [SOUND_MIXER_CD] = 0x12, - [SOUND_MIXER_VIDEO] = 0x14, - [SOUND_MIXER_LINE1] = 0x16, - [SOUND_MIXER_PCM] = 0x18, + [SOUND_MIXER_LINE] = AC97_LINEIN_VOL, + [SOUND_MIXER_CD] = AC97_CD_VOL, + [SOUND_MIXER_VIDEO] = AC97_VIDEO_VOL, + [SOUND_MIXER_LINE1] = AC97_AUX_VOL, + [SOUND_MIXER_PCM] = AC97_PCMOUT_VOL, /* 6 bit stereo */ - [SOUND_MIXER_VOLUME] = 0x02, - [SOUND_MIXER_PHONEOUT] = 0x04, + [SOUND_MIXER_VOLUME] = AC97_MASTER_VOL_STEREO, + [SOUND_MIXER_PHONEOUT] = AC97_HEADPHONE_VOL, /* 6 bit mono */ - [SOUND_MIXER_OGAIN] = 0x06, - [SOUND_MIXER_PHONEIN] = 0x0c, + [SOUND_MIXER_OGAIN] = AC97_MASTER_VOL_MONO, + [SOUND_MIXER_PHONEIN] = AC97_PHONE_VOL, /* 4 bit mono but shifted by 1 */ - [SOUND_MIXER_SPEAKER] = 0x08, + [SOUND_MIXER_SPEAKER] = AC97_MASTER_TONE, /* 6 bit mono + preamp */ - [SOUND_MIXER_MIC] = 0x0e, + [SOUND_MIXER_MIC] = AC97_MIC_VOL, /* 4 bit stereo */ - [SOUND_MIXER_RECLEV] = 0x1c, + [SOUND_MIXER_RECLEV] = AC97_RECORD_GAIN, /* 4 bit mono */ - [SOUND_MIXER_IGAIN] = 0x1e + [SOUND_MIXER_IGAIN] = AC97_RECORD_GAIN_MIC }; #ifdef OSS_DOCUMENTED_MIXER_SEMANTICS @@ -1049,8 +1192,8 @@ switch (ch) { case SOUND_MIXER_MIC: - j = rdcodec(s, 0x0e); - if (j & 0x8000) + j = rdcodec(s, AC97_MIC_VOL); + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x4949 - 0x202 * (j & 0x1f) + ((j & 0x40) ? 0x1b1b : 0), (int *)arg); @@ -1061,7 +1204,7 @@ case SOUND_MIXER_OGAIN: case SOUND_MIXER_PHONEIN: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x6464 - 0x303 * (j & 0x1f), (int *)arg); @@ -1075,7 +1218,7 @@ /* fall through */ case SOUND_MIXER_VOLUME: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); #ifdef AC97_PESSIMISTIC return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); @@ -1084,8 +1227,8 @@ #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_SPEAKER: - j = rdcodec(s, 0x0a); - if (j & 0x8000) + j = rdcodec(s, AC97_PCBEEP_VOL); + if (j & AC97_MUTE return put_user(0, (int *)arg); return put_user(0x6464 - ((j >> 1) & 0xf) * 0x606, (int *)arg); @@ -1095,7 +1238,7 @@ case SOUND_MIXER_LINE1: case SOUND_MIXER_PCM: j = rdcodec(s, volreg[ch]); - if (j & 0x8000) + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user(0x6464 - (swab(j) & 0x1f1f) * 3, (int *)arg); @@ -1103,23 +1246,23 @@ case SOUND_MIXER_TREBLE: if (!(s->mix.codec_id & CODEC_ID_BASSTREBLE)) return -EINVAL; - j = rdcodec(s, 0x08); + j = rdcodec(s, AC97_MASTER_TONE); if (ch == SOUND_MIXER_BASS) j >>= 8; return put_user((((j & 15) * 100) / 15) * 0x101, (int *)arg); /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ case SOUND_MIXER_RECLEV: - j = rdcodec(s, 0x1c); - if (j & 0x8000) + j = rdcodec(s, AC97_RECORD_GAIN); + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user((swab(j) & 0xf0f) * 6 + 0xa0a, (int *)arg); case SOUND_MIXER_IGAIN: if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) return -EINVAL; - j = rdcodec(s, 0x1e); - if (j & 0x8000) + j = rdcodec(s, AC97_RECORD_GAIN_MIC); + if (j & AC97_MUTE) return put_user(0, (int *)arg); return put_user((j & 0xf) * 0x606 + 0xa0a, (int *)arg); @@ -1174,7 +1317,7 @@ case SOUND_MIXER_LINE1: case SOUND_MIXER_PCM: if (l1 < 7 && r1 < 7) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 7) @@ -1191,7 +1334,7 @@ case SOUND_MIXER_VOLUME: #ifdef AC97_PESSIMISTIC if (l1 < 7 && r1 < 7) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 7) @@ -1202,7 +1345,7 @@ return 0; #else /* AC97_PESSIMISTIC */ if (l1 < 4 && r1 < 4) { - wrcodec(s, volreg[ch], 0x8000); + wrcodec(s, volreg[ch], AC97_MUTE); return 0; } if (l1 < 4) @@ -1216,21 +1359,21 @@ case SOUND_MIXER_OGAIN: case SOUND_MIXER_PHONEIN: #ifdef AC97_PESSIMISTIC - wrcodec(s, volreg[ch], (l1 < 7) ? 0x8000 : (100 - l1) / 3); + wrcodec(s, volreg[ch], (l1 < 7) ? AC97_MUTE : (100 - l1) / 3); return 0; #else /* AC97_PESSIMISTIC */ - wrcodec(s, volreg[ch], (l1 < 4) ? 0x8000 : (2 * (100 - l1) / 3)); + wrcodec(s, volreg[ch], (l1 < 4) ? AC97_MUTE : (2 * (100 - l1) / 3)); return 0; #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_SPEAKER: - wrcodec(s, 0x0a, (l1 < 10) ? 0x8000 : ((100 - l1) / 6) << 1); + wrcodec(s, AC97_PCBEEP_VOL, (l1 < 10) ? AC97_MUTE : ((100 - l1) / 6) << 1); return 0; case SOUND_MIXER_MIC: #ifdef AC97_PESSIMISTIC if (l1 < 11) { - wrcodec(s, 0x0e, 0x8000); + wrcodec(s, AC97_MIC_VOL, AC97_MUTE); return 0; } i = 0; @@ -1240,11 +1383,11 @@ } if (l1 < 11) l1 = 11; - wrcodec(s, 0x0e, ((73 - l1) / 2) | i); + wrcodec(s, AC97_MIC_VOL, ((73 - l1) / 2) | i); return 0; #else /* AC97_PESSIMISTIC */ if (l1 < 9) { - wrcodec(s, 0x0e, 0x8000); + wrcodec(s, AC97_MIC_VOL, AC97_MUTE); return 0; } i = 0; @@ -1254,37 +1397,37 @@ } if (l1 < 9) l1 = 9; - wrcodec(s, 0x0e, (((87 - l1) * 4) / 5) | i); + wrcodec(s, AC97_MIC_VOL, (((87 - l1) * 4) / 5) | i); return 0; #endif /* AC97_PESSIMISTIC */ case SOUND_MIXER_BASS: val = ((l1 * 15) / 100) & 0xf; - wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0x00ff) | (val << 8)); + wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0x00ff) | (val << 8)); return 0; case SOUND_MIXER_TREBLE: val = ((l1 * 15) / 100) & 0xf; - wrcodec(s, 0x08, (rdcodec(s, 0x08) & 0xff00) | val); + wrcodec(s, AC97_MASTER_TONE, (rdcodec(s, AC97_MASTER_TONE) & 0xff00) | val); return 0; /* SOUND_MIXER_RECLEV and SOUND_MIXER_IGAIN specify gain */ case SOUND_MIXER_RECLEV: if (l1 < 10 || r1 < 10) { - wrcodec(s, 0x1c, 0x8000); + wrcodec(s, AC97_RECORD_GAIN, AC97_MUTE); return 0; } if (l1 < 10) l1 = 10; if (r1 < 10) r1 = 10; - wrcodec(s, 0x1c, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); + wrcodec(s, AC97_RECORD_GAIN, (((l1 - 10) / 6) << 8) | ((r1 - 10) / 6)); return 0; case SOUND_MIXER_IGAIN: if (!(s->mix.codec_id & CODEC_ID_DEDICATEDMIC)) return -EINVAL; - wrcodec(s, 0x1e, (l1 < 10) ? 0x8000 : ((l1 - 10) / 6) & 0xf); + wrcodec(s, AC97_RECORD_GAIN_MIC, (l1 < 10) ? AC97_MUTE : ((l1 - 10) / 6) & 0xf); return 0; default: @@ -1302,8 +1445,8 @@ return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); if (val & 1) - wrcodec(s, 0x22, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); - val = rdcodec(s, 0x22); + wrcodec(s, AC97_3D_CONTROL, ((val << 3) & 0xf00) | ((val >> 1) & 0xf)); + val = rdcodec(s, AC97_3D_CONTROL); return put_user(((val & 0xf) << 1) | ((val & 0xf00) >> 3), (int *)arg); } if (cmd == SOUND_MIXER_INFO) { @@ -1330,7 +1473,7 @@ if (_IOC_DIR(cmd) == _IOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - return put_user(recsrc[rdcodec(s, 0x1a) & 7], (int *)arg); + return put_user(recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7], (int *)arg); case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ return put_user(SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_VIDEO | @@ -1377,10 +1520,10 @@ if (i == 0) return 0; /*val = mixer_recmask(s);*/ else if (i > 1) - val &= ~recsrc[rdcodec(s, 0x1a) & 7]; + val &= ~recsrc[rdcodec(s, AC97_RECORD_SELECT) & 7]; for (i = 0; i < 8; i++) { if (val & recsrc[i]) { - wrcodec(s, 0x1a, 0x101 * i); + wrcodec(s, AC97_RECORD_SELECT, 0x101 * i); return 0; } } @@ -1485,10 +1628,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac1rate; + tmo = 3 * HZ * (count + s->dma_dac1.fragsize) / 2 / s->dac1rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P1FMT) >> SCTRL_SH_P1FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1371: dma timed out??\n"); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "es1371: dac1 dma timed out??\n"); } remove_wait_queue(&s->dma_dac1.wait, &wait); current->state = TASK_RUNNING; @@ -1520,10 +1663,10 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->dac2rate; + tmo = 3 * HZ * (count + s->dma_dac2.fragsize) / 2 / s->dac2rate; tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; - if (!schedule_timeout(tmo ? : 1) && tmo) - printk(KERN_DEBUG "es1371: dma timed out??\n"); + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "es1371: dac2 dma timed out??\n"); } remove_wait_queue(&s->dma_dac2.wait, &wait); current->state = TASK_RUNNING; @@ -2467,6 +2610,7 @@ static ssize_t es1371_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2477,7 +2621,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -2488,15 +2635,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -2505,13 +2662,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t es1371_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct es1371_state *s = (struct es1371_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -2522,7 +2683,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -2535,15 +2699,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -2556,6 +2730,8 @@ es1371_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2697,6 +2873,44 @@ /* --------------------------------------------------------------------- */ +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef ES1371_DEBUG +static int proc_es1371_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data) +{ + int len = 0; + + struct es1371_state *s = devs; + int cnt; + + /* print out header */ + len += sprintf(buf + len, "\t\tCreative ES137x Debug Dump-o-matic\n"); + + /* print out CODEC state */ + len += sprintf (buf + len, "AC97 CODEC state\n"); + + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg:0x%02x val:0x%04x\n", cnt, rdcodec(s , cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* ES1371_DEBUG */ + +/* --------------------------------------------------------------------- */ + /* maximum number of devices */ #define NR_DEVICE 5 @@ -2740,12 +2954,11 @@ struct pci_dev *pcidev = NULL; mm_segment_t fs; int i, val, val2, index = 0; - u8 revision; unsigned cssr; if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.13 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.19 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2765,9 +2978,11 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = ES1371_MAGIC; s->io = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); if (check_region(s->io, ES1371_EXTENT)) { printk(KERN_ERR "es1371: io ports %#lx-%#lx in use\n", s->io, s->io+ES1371_EXTENT-1); goto err_region; @@ -2788,6 +3003,13 @@ goto err_dev3; if ((s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)) < 0) goto err_dev4; +#ifdef ES1371_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_entry("es1371", S_IFREG | S_IRUGO, NULL); + if (s->ps) + s->ps->read_proc = proc_es1371_dump; +#endif /* ES1371_DEBUG */ + /* initialize codec registers */ s->ctrl = 0; if ((joystick[index] & ~0x18) == 0x200) { @@ -2800,14 +3022,13 @@ s->sctrl = 0; cssr = 0; /* check to see if s/pdif mode is being requested */ - pci_read_config_byte(pcidev, PCI_REVISION_ID, &revision); if (spdif[index]) { - if (revision >= 4) { + if (s->rev >= 4) { printk(KERN_INFO "es1371: enabling S/PDIF output\n"); cssr |= STAT_EN_SPDIF; s->ctrl |= CTRL_SPDIFEN_B; } else { - printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", revision); + printk(KERN_ERR "es1371: revision %d does not support S/PDIF\n", s->rev); } } /* initialize the chips */ @@ -2820,35 +3041,12 @@ udelay(2); outl(s->ctrl, s->io+ES1371_REG_CONTROL); /* init the sample rate converter */ - outl(SRC_DIS, s->io + ES1371_REG_SRCONV); - for (val = 0; val < 0x80; val++) - src_write(s, val, 0); - src_write(s, SRCREG_DAC1+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC1+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_DAC2+SRCREG_TRUNC_N, 16 << 4); - src_write(s, SRCREG_DAC2+SRCREG_INT_REGS, 16 << 10); - src_write(s, SRCREG_VOL_ADC, 1 << 12); - src_write(s, SRCREG_VOL_ADC+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1, 1 << 12); - src_write(s, SRCREG_VOL_DAC1+1, 1 << 12); - src_write(s, SRCREG_VOL_DAC2, 1 << 12); - src_write(s, SRCREG_VOL_DAC2+1, 1 << 12); - set_adc_rate(s, 22050); - set_dac1_rate(s, 22050); - set_dac2_rate(s, 22050); - /* WARNING: - * enabling the sample rate converter without properly programming - * its parameters causes the chip to lock up (the SRC busy bit will - * be stuck high, and I've found no way to rectify this other than - * power cycle) - */ - wait_src_ready(s); - outl(0, s->io+ES1371_REG_SRCONV); + src_init(s); /* codec init */ - wrcodec(s, 0x00, 0); /* reset codec */ - s->mix.codec_id = rdcodec(s, 0x00); /* get codec ID */ - val = rdcodec(s, 0x7c); - val2 = rdcodec(s, 0x7e); + wrcodec(s, AC97_RESET, 0); /* reset codec */ + s->mix.codec_id = rdcodec(s, AC97_RESET); /* get codec ID */ + val = rdcodec(s, AC97_VENDOR_ID1); + val2 = rdcodec(s, AC97_VENDOR_ID2); printk(KERN_INFO "es1371: codec vendor %c%c%c revision %d\n", (val >> 8) & 0xff, val & 0xff, (val2 >> 8) & 0xff, val2 & 0xff); printk(KERN_INFO "es1371: codec features"); @@ -2875,6 +3073,7 @@ printk("%s\n", (s->mix.codec_id & 0x3ff) ? "" : " none"); val = (s->mix.codec_id >> CODEC_ID_SESHIFT) & CODEC_ID_SEMASK; printk(KERN_INFO "es1371: stereo enhancement: %s\n", (val <= 20) ? stereo_enhancement[val] : "unknown"); + fs = get_fs(); set_fs(KERNEL_DS); val = SOUND_MASK_LINE; @@ -2929,6 +3128,10 @@ while ((s = devs)) { devs = devs->next; +#ifdef ES1371_DEBUG + if (s->ps) + remove_proc_entry("es1371", NULL); +#endif /* ES1371_DEBUG */ outl(0, s->io+ES1371_REG_CONTROL); /* switch everything off */ outl(0, s->io+ES1371_REG_SERIAL_CONTROL); /* clear serial interrupts */ synchronize_irq(); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/esssolo1.c linux.14p2/drivers/sound/esssolo1.c --- linux.vanilla/drivers/sound/esssolo1.c Wed Oct 20 01:12:48 1999 +++ linux.14p2/drivers/sound/esssolo1.c Fri Oct 22 22:34:17 1999 @@ -54,12 +54,17 @@ * The fun part is that the Windows Solo1 driver doesn't * seem to do these tricks. * Bugs remaining: plops and clicks when starting/stopping playback + * 31.08.99 0.7 add spin_lock_init + * replaced current->state = x with set_current_state(x) + * 03.09.99 0.8 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race + * 07.10.99 0.9 Fix initialization; complain if sequencer writes time out + * Revised resource grabbing for the FM synthesizer * */ /*****************************************************************************/ -#include #include #include #include @@ -96,7 +101,7 @@ #define SOLO1_MAGIC ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1) -#define DDMABASE_OFFSET 0x10 /* chip bug workaround kludge */ +#define DDMABASE_OFFSET 0 /* chip bug workaround kludge */ #define DDMABASE_EXTENT 16 #define IOBASE_EXTENT 16 @@ -105,6 +110,7 @@ #define MPUBASE_EXTENT 4 #define GPBASE_EXTENT 4 +#define FMSYNTH_EXTENT 4 /* MIDI buffer sizes */ @@ -137,6 +143,8 @@ #define wait_queue_head_t struct wait_queue * #define init_waitqueue_head(w) *(w) = 0 #define init_MUTEX(m) *(m) = MUTEX +#define __set_current_state(x) do { current->state = (x); } while (0) +#define set_current_state(x) __set_current_state(x) #endif /* --------------------------------------------------------------------- */ @@ -226,7 +234,7 @@ { int i; unsigned long flags; - + /* the __cli stunt is to send the data within the command window */ for (i = 0; i < 0xffff; i++) { __save_flags(flags); @@ -238,6 +246,8 @@ } __restore_flags(flags); } + printk(KERN_ERR "esssolo1: write_seq timeout\n"); + outb(data, s->sbbase+0xc); } extern inline int read_seq(struct solo1_state *s, unsigned char *data) @@ -251,6 +261,7 @@ *data = inb(s->sbbase+0xa); return 1; } + printk(KERN_ERR "esssolo1: read_seq timeout\n"); return 0; } @@ -952,7 +963,7 @@ if (s->dma_dac.mapped) return 0; - current->state = TASK_INTERRUPTIBLE; + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&s->dma_dac.wait, &wait); for (;;) { spin_lock_irqsave(&s->lock, flags); @@ -964,7 +975,7 @@ break; if (nonblock) { remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); return -EBUSY; } tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->rate; @@ -976,7 +987,7 @@ printk(KERN_DEBUG "solo1: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; return 0; @@ -1622,6 +1633,7 @@ static ssize_t solo1_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1632,7 +1644,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -1643,15 +1658,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -1660,13 +1685,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t solo1_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct solo1_state *s = (struct solo1_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); ssize_t ret; unsigned long flags; unsigned ptr; @@ -1677,7 +1706,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -1690,15 +1722,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -1711,6 +1753,8 @@ solo1_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -1979,6 +2023,12 @@ return -ERESTARTSYS; down(&s->open_sem); } + if (check_region(s->sbbase, FMSYNTH_EXTENT)) { + up(&s->open_sem); + printk(KERN_ERR "solo1: FM synth io ports in use, opl3 loaded?\n"); + return -EBUSY; + } + request_region(s->sbbase, FMSYNTH_EXTENT, "ESS Solo1"); /* init the stuff */ outb(1, s->sbbase); outb(0x20, s->sbbase+1); /* enable waveforms */ @@ -2006,6 +2056,7 @@ outb(regb, s->sbbase+2); outb(0, s->sbbase+3); } + release_region(s->sbbase, FMSYNTH_EXTENT); up(&s->open_sem); wake_up(&s->open_wait); MOD_DEC_USE_COUNT; @@ -2062,7 +2113,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "solo1: version v0.6 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "solo1: version v0.9 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, pcidev))) { if (pcidev->base_address[0] == 0 || @@ -2087,6 +2138,7 @@ init_waitqueue_head(&s->midi.iwait); init_waitqueue_head(&s->midi.owait); init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); s->magic = SOLO1_MAGIC; s->pcidev = pcidev; s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; @@ -2097,14 +2149,14 @@ s->gpbase = pcidev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK; s->irq = pcidev->irq; if (check_region(s->iobase, IOBASE_EXTENT) || - check_region(s->sbbase+4, SBBASE_EXTENT-4) || + check_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT) || check_region(s->ddmabase, DDMABASE_EXTENT) || check_region(s->mpubase, MPUBASE_EXTENT)) { printk(KERN_ERR "solo1: io ports in use\n"); goto err_region; } request_region(s->iobase, IOBASE_EXTENT, "ESS Solo1"); - request_region(s->sbbase+4, SBBASE_EXTENT-4, "ESS Solo1"); /* allow OPL3 synth module */ + request_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT, "ESS Solo1"); /* allow OPL3 synth module */ request_region(s->ddmabase, DDMABASE_EXTENT, "ESS Solo1"); request_region(s->mpubase, MPUBASE_EXTENT, "ESS Solo1"); if (request_irq(s->irq, solo1_interrupt, SA_SHIRQ, "ESS Solo1", s)) { @@ -2131,6 +2183,10 @@ if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) goto err_dev4; /* initialize the chips */ + if (!reset_ctrl(s)) { + printk(KERN_ERR "esssolo1: cannot reset controller\n"); + goto err; + } outb(0xb0, s->iobase+7); /* enable A1, A2, MPU irq's */ /* initialize mixer regs */ @@ -2165,6 +2221,8 @@ index++; continue; + err: + unregister_sound_dsp(s->dev_dmfm); err_dev4: unregister_sound_dsp(s->dev_midi); err_dev3: @@ -2176,7 +2234,7 @@ free_irq(s->irq, s); err_irq: release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); err_region: @@ -2206,7 +2264,7 @@ pci_write_config_word(s->pcidev, 0x60, 0); /* turn off DDMA controller address space */ free_irq(s->irq, s); release_region(s->iobase, IOBASE_EXTENT); - release_region(s->sbbase+4, SBBASE_EXTENT-4); + release_region(s->sbbase+FMSYNTH_EXTENT, SBBASE_EXTENT-FMSYNTH_EXTENT); release_region(s->ddmabase, DDMABASE_EXTENT); release_region(s->mpubase, MPUBASE_EXTENT); unregister_sound_dsp(s->dev_audio); @@ -2222,5 +2280,4 @@ module_init(init_solo1); module_exit(cleanup_solo1); - diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/lowlevel/miroaci.h linux.14p2/drivers/sound/lowlevel/miroaci.h --- linux.vanilla/drivers/sound/lowlevel/miroaci.h Sun Aug 23 21:32:25 1998 +++ linux.14p2/drivers/sound/lowlevel/miroaci.h Fri Oct 22 22:19:05 1999 @@ -1,6 +1,11 @@ +#ifdef CONFIG_ACI_MIXER extern int aci_implied_cmd(unsigned char opcode); extern int aci_write_cmd(unsigned char opcode, unsigned char parameter); extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2); extern int aci_read_cmd(unsigned char opcode, int length, unsigned char *parameter); extern int aci_indexed_cmd(unsigned char opcode, unsigned char index, unsigned char *parameter); +#else +#error Compiling a driver that needs the ACI-mixer but without ACI-mixer support + +#endif diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/msnd_pinnacle.c linux.14p2/drivers/sound/msnd_pinnacle.c --- linux.vanilla/drivers/sound/msnd_pinnacle.c Wed Oct 20 01:12:48 1999 +++ linux.14p2/drivers/sound/msnd_pinnacle.c Fri Oct 22 21:25:36 1999 @@ -46,6 +46,7 @@ # include #endif #include +#include #include "sound_config.h" #include "sound_firmware.h" #ifdef MSND_CLASSIC diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/nm256_audio.c linux.14p2/drivers/sound/nm256_audio.c --- linux.vanilla/drivers/sound/nm256_audio.c Wed Oct 20 01:12:48 1999 +++ linux.14p2/drivers/sound/nm256_audio.c Fri Oct 22 21:25:36 1999 @@ -14,6 +14,8 @@ #include #include #include +#include + #include "sound_config.h" #include "soundmodule.h" #include "nm256.h" diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/opl3.c linux.14p2/drivers/sound/opl3.c --- linux.vanilla/drivers/sound/opl3.c Wed Aug 19 22:48:30 1998 +++ linux.14p2/drivers/sound/opl3.c Fri Oct 22 22:52:53 1999 @@ -32,8 +32,6 @@ #include "sound_config.h" #include "soundmodule.h" -#ifdef CONFIG_YM3812 - #include "opl3.h" #define MAX_VOICE 18 @@ -1223,5 +1221,3 @@ EXPORT_SYMBOL(opl3_init); EXPORT_SYMBOL(opl3_detect); MODULE_PARM(io, "i"); - -#endif diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/sound/sonicvibes.c linux.14p2/drivers/sound/sonicvibes.c --- linux.vanilla/drivers/sound/sonicvibes.c Wed Oct 20 01:12:48 1999 +++ linux.14p2/drivers/sound/sonicvibes.c Fri Oct 22 22:34:17 1999 @@ -71,6 +71,14 @@ * 15.06.99 0.15 Fix bad allocation bug. * Thanks to Deti Fliegl * 28.06.99 0.16 Add pci_set_master + * 03.08.99 0.17 adapt to Linus' new __setup/__initcall + * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" + * 12.08.99 0.18 module_init/__setup fixes + * 24.08.99 0.19 get rid of the dmaio kludge, replace with allocate_resource + * 31.08.99 0.20 add spin_lock_init + * __initlocaldata to fix gcc 2.7.x problems + * 03.09.99 0.21 change read semantics for MIDI to match + * OSS more closely; remove possible wakeup race * */ @@ -1265,9 +1273,9 @@ current->state = TASK_RUNNING; return -EBUSY; } - tmo = (count * HZ) / s->ratedac; + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 / s->ratedac; tmo >>= sample_shift[(s->fmt >> SV_CFMT_ASHIFT) & SV_CFMT_MASK]; - if (!schedule_timeout(tmo ? : 1) && tmo) + if (!schedule_timeout(tmo + 1)) printk(KERN_DEBUG "sv: dma timed out??\n"); } remove_wait_queue(&s->dma_dac.wait, &wait); @@ -1870,6 +1878,7 @@ static ssize_t sv_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1880,7 +1889,10 @@ return -ESPIPE; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.iwait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.ird; @@ -1891,15 +1903,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.iwait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) - return ret ? ret : -EFAULT; + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIINBUF; spin_lock_irqsave(&s->lock, flags); s->midi.ird = ptr; @@ -1908,13 +1930,17 @@ count -= cnt; buffer += cnt; ret += cnt; + break; } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.iwait, &wait); return ret; } static ssize_t sv_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct sv_state *s = (struct sv_state *)file->private_data; + struct wait_queue wait = { current, NULL }; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1925,7 +1951,10 @@ return -ESPIPE; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + if (count == 0) + return 0; ret = 0; + add_wait_queue(&s->midi.owait, &wait); while (count > 0) { spin_lock_irqsave(&s->lock, flags); ptr = s->midi.owr; @@ -1938,15 +1967,25 @@ if (cnt > count) cnt = count; if (cnt <= 0) { - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - interruptible_sleep_on(&s->midi.owait); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } continue; } - if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) - return ret ? ret : -EFAULT; + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + break; + } ptr = (ptr + cnt) % MIDIOUTBUF; spin_lock_irqsave(&s->lock, flags); s->midi.owr = ptr; @@ -1959,6 +1998,8 @@ sv_handle_midi(s); spin_unlock_irqrestore(&s->lock, flags); } + current->state = TASK_RUNNING; + remove_wait_queue(&s->midi.owait, &wait); return ret; } @@ -2324,7 +2365,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.16 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.21 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); @@ -2353,6 +2394,7 @@ init_waitqueue(&s->midi.iwait); init_waitqueue(&s->midi.owait); s->open_sem = MUTEX; + spin_lock_init(&s->lock); s->magic = SV_MAGIC; s->iosb = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK; s->ioenh = pcidev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK; @@ -2423,8 +2465,8 @@ wrindir(s, SV_CIDRIVECONTROL, 0); /* drive current 16mA */ wrindir(s, SV_CIENABLE, s->enable = 0); /* disable DMAA and DMAC */ outb(~(SV_CINTMASK_DMAA | SV_CINTMASK_DMAC), s->ioenh + SV_CODEC_INTMASK); - //outb(0xff, s->iodmaa + SV_DMA_RESET); - //outb(0xff, s->iodmac + SV_DMA_RESET); + /* outb(0xff, s->iodmaa + SV_DMA_RESET); */ + /* outb(0xff, s->iodmac + SV_DMA_RESET); */ inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ wrindir(s, SV_CIADCCLKSOURCE, 0); /* use pll as ADC clock source */ wrindir(s, SV_CIANALOGPWRDOWN, 0); /* power up the analog parts of the device */ @@ -2524,8 +2566,8 @@ synchronize_irq(); inb(s->ioenh + SV_CODEC_STATUS); /* ack interrupts */ wrindir(s, SV_CIENABLE, 0); /* disable DMAA and DMAC */ - //outb(0, s->iodmaa + SV_DMA_RESET); - //outb(0, s->iodmac + SV_DMA_RESET); + /*outb(0, s->iodmaa + SV_DMA_RESET);*/ + /*outb(0, s->iodmac + SV_DMA_RESET);*/ free_irq(s->irq, s); release_region(s->iodmac, SV_EXTENT_DMA); release_region(s->iodmaa, SV_EXTENT_DMA); diff -u --new-file --exclude-from ../exclude --recursive linux.vanilla/drivers/video/matroxfb.c linux.14p2/drivers/video/matroxfb.c --- linux.vanilla/drivers/video/matroxfb.c Sat Aug 14 02:26:17 1999 +++ linux.14p2/drivers/video/matroxfb.c Wed Oct 27 18:41:36 1999 @@ -1,10 +1,10 @@ /* * - * Hardware accelerated Matrox Millennium I, II, Mystique and G200 + * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 * * (c) 1998,1999 Petr Vandrovec * - * Version: 1.15 1999/04/19 + * Version: 1.19a 1999/08/12 (2.2.x branch) * * MTRR stuff: 1998 Tom Rini * @@ -14,8 +14,8 @@ * "Kurt Garloff" * Betatesting, fixes, ideas, videomodes, videomodes timmings * - * "Tom Rini" - * MTRR stuff, betatesting, fixes, ideas + * "Tom Rini" + * MTRR stuff, PPC cleanups, betatesting, fixes, ideas * * "Bibek Sahu" * Access device through readb|w|l and write b|w|l @@ -60,6 +60,9 @@ * "Cort Dougan" * CHRP fixes and PReP cleanup * + * "Mark Vojkovich" + * G400 support + * * (following author is not in any relation with this code, but his code * is included in this driver) * @@ -87,6 +90,11 @@ /* Debug register calls, too? */ #undef MATROXFB_DEBUG_REG +/* Log reentrancy attempts - you must have printstate() patch applied */ +#undef MATROXFB_DEBUG_REENTER +/* you must define DEBUG_REENTER to get debugged CONSOLEBH... */ +#undef MATROXFB_DEBUG_CONSOLEBH + #include #include #include @@ -127,12 +135,15 @@ #include