]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.2.14pre2 2.2.14pre2
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:08 +0000 (15:20 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:08 +0000 (15:20 -0500)
o Back out buggy masquerade tweak
o Add Matrox G400 fb support (Petr Vandrovec)
o a.out improvements (Andrea Arcangeli)
o More build fixes (Arjan van de Ven)
o Report new intel CPU caches correctly (Dragan Stancevic)
o Fix doubled io_apic_setup extern (lots of folks)
o Updated list of documentation about the kernel (Juan-Mariano de Goyeneche)
o IDE/SMP fixes (Mark Lord)
| This isnt Andre's big stuff - yet...
o Quota fix (Jan Kara)
o Handle NEC 260 IDE CD right (David Woodhouse)
o Masquerade fix (Joseph Gooch)
o PCI parallel port handling (Tim Waugh)
o Tulip 0.91g (Donald Becker)

47 files changed:
CREDITS
Documentation/kernel-docs.txt
arch/alpha/kernel/ptrace.c
arch/alpha/kernel/signal.c
arch/alpha/mm/fault.c
arch/i386/kernel/bios32.c
arch/i386/kernel/ptrace.c
arch/i386/kernel/setup.c
arch/i386/kernel/signal.c
arch/i386/mm/fault.c
drivers/block/ide-cd.c
drivers/block/ide-disk.c
drivers/block/ide-dma.c
drivers/block/ide-floppy.c
drivers/block/ide-probe.c
drivers/block/ide-tape.c
drivers/block/ide.c
drivers/block/ide.h
drivers/block/pdc4030.c
drivers/char/Makefile
drivers/misc/parport_pc.c
drivers/misc/parport_share.c
drivers/net/hamradio/Config.in
drivers/net/sis900.h
drivers/net/tulip.c
drivers/scsi/ide-scsi.c
drivers/video/matroxfb.c
fs/binfmt_aout.c
fs/dquot.c
include/asm-i386/sgi-cobalt.h [new file with mode: 0644]
include/linux/fb.h
include/linux/i2c.h
include/linux/mm.h
include/linux/parport.h
include/linux/parport_pc.h
include/linux/pci.h
include/linux/swap.h
ipc/shm.c
mm/filemap.c
mm/memory.c
mm/page_alloc.c
mm/swapfile.c
mm/vmscan.c
net/ipv4/ip_forward.c
net/ipv4/ip_masq.c
net/ipv4/ip_masq_user.c
net/irda/irmod.c

diff --git a/CREDITS b/CREDITS
index 6aa113c223c187df7d8b771f5780d174a457b7c6..cf41b2e647efec3cbae5546f8a0474ccf5f3ce53 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2122,9 +2122,10 @@ S: The Netherlands
 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
index f78256a27cf2a2fadcb231bd2d7343eb824929db..e56e465e8885fd260fef6c7e52ce3e5e709a02cb 100644 (file)
@@ -1,15 +1,14 @@
 
-   
- 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
-    */
+       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
    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://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)
    
-   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
+     * 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.
        
-     * MISCELLANEOUS:
-       
-          + 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.
+     * 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
index 18c9a8b13dc04b68476007e1aee64a65669e6136..4e2f7359b286c0adb79f62ea7ad9db4d390ffd70 100644 (file)
@@ -149,13 +149,18 @@ get_long(struct task_struct * tsk, struct vm_area_struct * vma,
        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 @@ get_long(struct task_struct * tsk, struct vm_area_struct * vma,
        }
        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 @@ get_long(struct task_struct * tsk, struct vm_area_struct * vma,
        }
        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 @@ put_long(struct task_struct * tsk, struct vm_area_struct * vma,
        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 @@ put_long(struct task_struct * tsk, struct vm_area_struct * vma,
        }
        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 @@ put_long(struct task_struct * tsk, struct vm_area_struct * vma,
        }
        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.  */
index 832d72ee0de497f87115d430eb6bd68e255bef62..cc07dfae02845697d77cceaf6db4e7e3fab7dc73 100644 (file)
@@ -437,6 +437,8 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
                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 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        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.  */
index ac414259a20929bea85dc090e97d8f2e0ce6da4d..da992616173cf3de5f22806318b249de52e5cc8a 100644 (file)
@@ -120,9 +120,18 @@ good_area:
                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 @@ bad_area:
 
        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 @@ bad_area:
                printk("%s: Exception at [<%lx>] (%lx)\n",
                       current->comm, regs->pc, newpc);
                regs->pc = newpc;
-               goto out;
+               goto out_unlock;
        }
 
 /*
@@ -154,7 +164,37 @@ bad_area:
               "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;
+}
index 44a8b61f3c042ceb1f915fe8706c615a66241f35..2bc228a769567a5be8bd29d9c1a384adbf9f6d1c 100644 (file)
@@ -1095,9 +1095,8 @@ static void __init pcibios_scan_buglist(struct pci_bus *b)
  * 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)
 {
        struct pci_dev *dev;
index 9f5ce58f148dfded48c2ea78e94f6ff5bdb50823..1eb33a82d992409b075aee842b8ce71d8b083c1e 100644 (file)
@@ -80,12 +80,17 @@ static unsigned long get_long(struct task_struct * tsk,
        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 @@ repeat:
        }
        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 @@ repeat:
        }
        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 @@ static void put_long(struct task_struct * tsk, struct vm_area_struct * vma, unsi
        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 @@ repeat:
        }
        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 @@ repeat:
        }
        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)
index b225637b8c37be789f7ce0db7796fcc615ac799e..08795e0942d38ddb2853977f020a6e6549566f99 100644 (file)
@@ -17,6 +17,9 @@
  *
  *  IDT Winchip tweaks, misc clean ups.
  *     Dave Jones <dave@powertweak.com>, August 1999
+ *
+ *     Added proper L2 cache detection for Coppermine
+ *     Dragan Stancevic <visitor@valinux.com>, October 1999
  */
 
 /*
@@ -838,6 +841,7 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c))
                                        break;
 
                                case 0x42:
+                               case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ 
                                        cache_size = 256;
                                        break;
 
index 32e7c4c56f4a7c87edbb9339a1953b4cee769435..5a7d8bbaea1fdb16e1cdd95cd940e05d367a2dc9 100644 (file)
@@ -419,13 +419,19 @@ static void setup_frame(int sig, struct k_sigaction *ka,
                           ? 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 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        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 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        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.  */
index 5a1f363bdf57738ec06b39fa619f064035163d7b..6a8d26b6fd754cb9a8f3ced2ece4c76293d95d13 100644 (file)
@@ -50,7 +50,14 @@ good_area:
        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 @@ check_stack:
 
 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 @@ good_area:
         * 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 @@ no_context:
  * 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);
 
index 7ccc83543f96b9862cae088531bec7a0ec70e06e..e543f4b255d823d5f06408642877242e64eb43e6 100644 (file)
@@ -519,7 +519,7 @@ static void cdrom_end_request (int uptodate, ide_drive_t *drive)
 
 /* 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 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
                                                      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 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
                        } 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 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
        }
 
        /* Retry, or handle the next request. */
+       *startstop = ide_stopped;
        return 1;
 }
 
@@ -660,13 +661,15 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
    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 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
        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 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
    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 @@ static int cdrom_transfer_packet_command (ide_drive_t *drive,
                /* 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 @@ static int cdrom_transfer_packet_command (ide_drive_t *drive,
        /* Send the command to the device. */
        atapi_output_bytes (drive, cmd_buf, cmd_len);
 
-       return 0;
+       return ide_started;
 }
 
 
@@ -826,12 +830,13 @@ int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
 /*
  * 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 @@ static void cdrom_read_intr (ide_drive_t *drive)
                        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 @@ static void cdrom_read_intr (ide_drive_t *drive)
                                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 @@ static void cdrom_read_intr (ide_drive_t *drive)
                        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 @@ static void cdrom_read_intr (ide_drive_t *drive)
                        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 @@ static void cdrom_read_intr (ide_drive_t *drive)
        /* Done moving data!
           Wait for another interrupt. */
        ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
+       return ide_started;
 }
 
 
@@ -1019,7 +1026,7 @@ static int cdrom_read_from_buffer (ide_drive_t *drive)
  * 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 @@ static void cdrom_start_read_continuation (ide_drive_t *drive)
                        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 @@ static void cdrom_start_read_continuation (ide_drive_t *drive)
        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 @@ static void cdrom_seek_intr (ide_drive_t *drive)
                        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 @@ static void cdrom_start_seek_continuation (ide_drive_t *drive)
        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 @@ static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
 
        /* 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 @@ static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
                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 @@ cdrom_lockdoor (ide_drive_t *drive, int lockflag,
 
 
 /* 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 @@ static void cdrom_pc_intr (ide_drive_t *drive)
                        pc->stat = 1;
                        cdrom_end_request (1, drive);
                }
-               return;
+               return ide_stopped;
        }
 
        /* Figure out how much data to transfer. */
@@ -1288,21 +1296,21 @@ static void cdrom_pc_intr (ide_drive_t *drive)
 
        /* 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 @@ static void cdrom_do_packet_command (ide_drive_t *drive)
        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 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
 /****************************************************************************
  * 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 @@ void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long bloc
                        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 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
                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)
index cd37e6a720e25fb637e0e799425b27d51a9a76ff..34b6fe51ab7e48ed6b835f1208a28958405a3930 100644 (file)
@@ -122,7 +122,7 @@ static int lba_capacity_is_ok (struct hd_driveid *id)
 /*
  * 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;
@@ -130,8 +130,7 @@ static void read_intr (ide_drive_t *drive)
        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;
        
@@ -143,12 +142,6 @@ read_next:
                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",
@@ -165,21 +158,24 @@ read_next:
                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,
@@ -196,25 +192,28 @@ static void write_intr (ide_drive_t *drive)
                        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;
@@ -226,6 +225,7 @@ void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
                        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) {
@@ -233,32 +233,36 @@ void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
                                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? */
@@ -267,20 +271,17 @@ static void multwrite_intr (ide_drive_t *drive)
                                        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();
 
@@ -291,28 +292,31 @@ static void set_multmode_intr (ide_drive_t *drive)
                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;
 }
 
 /*
@@ -320,7 +324,7 @@ static void recal_intr (ide_drive_t *drive)
  * 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);
@@ -366,45 +370,61 @@ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long bl
        }
 #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)
@@ -458,7 +478,7 @@ static unsigned long idedisk_capacity (ide_drive_t  *drive)
        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;
 
@@ -484,7 +504,9 @@ static void idedisk_special (ide_drive_t *drive)
                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)
@@ -606,7 +628,7 @@ static int set_nowerr(ide_drive_t *drive, int arg)
                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;
 }
 
@@ -764,7 +786,7 @@ static void idedisk_setup (ide_drive_t *drive)
        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 {
index 17f46f464f85ef818f965fd78cce0658756d0257..8bc9cbc53dc75036154d41fce10a0335e1f8995a 100644 (file)
@@ -136,7 +136,7 @@ const char *bad_dma_drives[] = {"WDC AC11000H",
 /*
  * 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 @@ void ide_dma_intr (ide_drive_t *drive)
                                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);
 }
 
 /*
index b70bc5f43a7e42cb0df3c3c8f1f005565eb481b8..bc6637bc3aaf920471279a48d68dd84d9ee1bba4 100644 (file)
@@ -834,7 +834,7 @@ static void idefloppy_retry_pc (ide_drive_t *drive)
  *     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 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
                        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 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
 
        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 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
                                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 @@ static void idefloppy_pc_intr (ide_drive_t *drive)
        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 @@ static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
                }
                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 @@ static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
        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 @@ static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t
 /*
  *     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 @@ static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsign
                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 @@ static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsign
                        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 @@ static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsign
                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);
 }
 
 /*
index 1d13043e8a76e5e4b22e044cd9f02f391877c243..3197862016ad12c3c1a9fcbfdab69aee4d99c670 100644 (file)
@@ -560,10 +560,6 @@ static int init_irq (ide_hwif_t *hwif)
                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;
index 8da21f1627ac6b689b4bad856ce271d8fbd4f624..f865a7d27f7ff87defc4ab6219d5fe64439a53e8 100644 (file)
@@ -522,7 +522,7 @@ typedef struct idetape_packet_command_s {
        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_analyze_error (ide_drive_t *drive,idetape_request_sense_resu
        }
 }
 
-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 @@ static void idetape_request_sense_callback (ide_drive_t *drive)
                printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
                idetape_end_request (0,HWGROUP (drive));
        }
+       return ide_stopped;
 }
 
 /*
@@ -1701,7 +1702,7 @@ static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
  *     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 @@ static void idetape_retry_pc (ide_drive_t *drive)
        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 @@ static void idetape_retry_pc (ide_drive_t *drive)
  *     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 @@ static void idetape_pc_intr (ide_drive_t *drive)
 #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 @@ static void idetape_pc_intr (ide_drive_t *drive)
                        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 @@ static void idetape_pc_intr (ide_drive_t *drive)
 
        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 @@ static void idetape_pc_intr (ide_drive_t *drive)
                                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 @@ static void idetape_pc_intr (ide_drive_t *drive)
        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_pc_intr (ide_drive_t *drive)
  *
  */
 
-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 @@ static void idetape_transfer_pc(ide_drive_t *drive)
        }
        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 @@ static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
                        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 @@ static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
        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 @@ static void idetape_media_access_finished (ide_drive_t *drive)
        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 @@ static void idetape_media_access_finished (ide_drive_t *drive)
                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 @@ static void idetape_pc_callback (ide_drive_t *drive)
 #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 @@ static void idetape_rw_callback (ide_drive_t *drive)
                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 @@ static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, un
                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 @@ static void idetape_read_position_callback (ide_drive_t *drive)
                }
        } 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 @@ static void idetape_create_read_position_cmd (idetape_pc_t *pc)
 /*
  *     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 @@ static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned
                /*
                 *      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 @@ static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned
                        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 @@ static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned
                        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);
 }
 
 /*
index 74043c67e9aa02c191c0db5c1ccd2b2a72ed53f4..3f43fc174f28bd200dbcce171c58237b19e72198 100644 (file)
@@ -433,7 +433,7 @@ static inline int drive_is_ready (ide_drive_t *drive)
 #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 @@ void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int t
        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 @@ void ide_geninit (struct gendisk *gd)
        }
 }
 
-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 @@ static void do_reset1 (ide_drive_t *, int);         /* needed below */
  * 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 @@ static void atapi_reset_pollfunc (ide_drive_t *drive)
        } 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 @@ static void atapi_reset_pollfunc (ide_drive_t *drive)
  * 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 @@ static void reset_pollfunc (ide_drive_t *drive)
        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 @@ static void reset_pollfunc (ide_drive_t *drive)
                }
        }
        hwgroup->poll_timeout = 0;      /* done polling */
+       return ide_stopped;
 }
 
 static void pre_reset (ide_drive_t *drive)
@@ -623,7 +622,7 @@ static void pre_reset (ide_drive_t *drive)
  * (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 @@ static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
                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 @@ static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
 #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 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
        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 @@ static void try_to_flush_leftover_data (ide_drive_t *drive)
 /*
  * 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 @@ void ide_error (ide_drive_t *drive, const char *msg, byte stat)
                        /* 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 @@ void ide_error (ide_drive_t *drive, const char *msg, byte stat)
        } 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 @@ void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 /*
  * 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 @@ static void drive_cmd_intr (ide_drive_t *drive)
                        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 @@ static inline void do_special (ide_drive_t *drive)
                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 @@ static inline void do_special (ide_drive_t *drive)
  * 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 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
                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 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
                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 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
  * 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 @@ static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
                }
                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 @@ static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
                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 @@ static inline void start_request (ide_drive_t *drive)
        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 @@ repeat:
 }
 
 /*
- * 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;
 
-       hwgroup->busy = 1;
-       while (hwgroup->handler == NULL) {
-               spin_lock_irqsave(&io_request_lock, io_flags);
+       ide_get_lock(&ide_lock, ide_intr, hwgroup);     /* for atari only: POSSIBLY BROKEN HERE(?) */
+
+       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 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags
                                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 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags
                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 @@ struct request **ide_get_queue (kdev_t dev)
        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 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
        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_intr (int irq, void *dev_id, struct pt_regs *regs)
                         */
                        (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 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
        rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
        if (action == ide_wait)
                rq->sem = &sem;
-
        spin_lock_irqsave(&io_request_lock, flags);
        cur_rq = drive->queue;
        if (cur_rq == NULL || action == ide_preempt) {
@@ -1537,16 +1573,13 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
                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 @@ int ide_revalidate_disk(kdev_t i_rdev)
        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<<PARTN_BITS); ++p) {
                if (drive->part[p].nr_sects > 0) {
@@ -1962,7 +1995,7 @@ int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
        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 @@ int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
                                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 @@ int ide_spin_wait_hwgroup (ide_drive_t *drive, unsigned long *flags)
        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 @@ int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
                                *p = val;
                        break;
        }
-       spin_unlock_irqrestore(&HWGROUP(drive)->spinlock, flags);
+       spin_unlock_irqrestore(&io_request_lock, flags);
        return 0;
 }
 
@@ -2844,9 +2886,10 @@ static int default_cleanup (ide_drive_t *drive)
        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 @@ static unsigned long default_capacity (ide_drive_t *drive)
        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)
index bc7d7fbdd37e5b0d12867647bc4fa3b37247a051..203c74c36a91cfde8d8191825817c624ed500338 100644 (file)
@@ -352,15 +352,23 @@ typedef struct hwif_s {
 #endif
        } 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 @@ read_proc_t proc_ide_read_geometry;
 #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 void        (ide_release_proc)(struct inode *, struct file *, ide_drive_t *);
 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 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
 
 /*
  * 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 @@ void ide_fixstring (byte *s, const int bytecount, const int byteswap);
  * 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 @@ int ide_xlate_1024 (kdev_t, int, const char *);
  * 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 @@ int ide_system_bus_speed (void);
  * 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 @@ void ide_scan_pcibus (void) __init;
 #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);
index e032da579f5775d8d4cf620aa640f42d8090e585..ef1c1e2fb823066919ad6c2d646acd422f5a38de 100644 (file)
@@ -122,6 +122,7 @@ Returns: 0 if no Promise card present at this io_base
 */
 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 @@ int init_pdc4030 (void)
        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 @@ int init_pdc4030 (void)
 /*
  * 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 @@ static void promise_read_intr (ide_drive_t *drive)
        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 @@ read_next:
                    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 @@ static void promise_write_pollfunc (ide_drive_t *drive)
         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 @@ static void promise_write_pollfunc (ide_drive_t *drive)
  * 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 @@ static void promise_write (ide_drive_t *drive)
  * 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 @@ void do_pdc4030_io (ide_drive_t *drive, struct request *rq)
                     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;
 }
index 5ac1944e135d066d61cf95bdbb0122c85b6e75fa..3313a98e5812f1d813d3bcbaec324ec1955f5b20 100644 (file)
@@ -380,9 +380,11 @@ endif
 
 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
 
index 4222c89d3ca350d2928d46accd79a0ecf22e1568..d9119e2cc4bf20361b1b26b40ed3da72ac4e3e5d 100644 (file)
@@ -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 <linux/config.h>
@@ -43,6 +46,7 @@
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/malloc.h>
+#include <linux/pci.h>
 
 #include <asm/io.h>
 
@@ -62,27 +66,27 @@ static void parport_pc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
 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 @@ int parport_pc_check_epp_timeout(struct parport *p)
 
 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 @@ unsigned char parport_pc_frob_control(struct parport *p, unsigned char mask,  un
        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_change_mode(struct parport *p, int m)
 
 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 @@ void parport_pc_release_resources(struct parport *p)
                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 @@ int parport_pc_claim_resources(struct parport *p)
                        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 parport_pc_epp_read_block(struct parport *p, void *buf, size_t length)
 {
        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 parport_pc_epp_write_block(struct parport *p, void *buf, size_t length)
 {
        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 @@ static int parport_SPP_supported(struct parport *pb)
         * 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 @@ out:
 
 /* --- 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 @@ static int probe_one_port(unsigned long int base, int irq, int dma)
                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 @@ static int probe_one_port(unsigned long int base, int irq, int dma)
        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 @@ static int probe_one_port(unsigned long int base, int irq, int dma)
        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 @@ int parport_pc_init(int *io, int *irq, int *dma)
                /* 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;
index e63d7611f2a406b3a765d26f4b4f542693e1ac10..d9e54eb49986f2d796f1953bc7459d644ba4df9d 100644 (file)
@@ -108,6 +108,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
        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) {
index 869c3710078bf93c8481e2f1ec08043b8e2fff39..7e2fc8f4fe237554a91728e8e35438159c7ff7ac 100644 (file)
@@ -11,12 +11,12 @@ if [ "$CONFIG_SCC" != "n" ]; then
   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
index e3adaaeff78549b167cf58746d9df2dc5acc1ae4..bb11533f957f213191281d2c3c085478e8ca491e 100644 (file)
@@ -246,7 +246,7 @@ enum sis900_eeprom_command {
 #define TRUE            1
 #define FALSE           0
 
-/* PCI stuff, should be move to pic.h */
+/* PCI stuff, should be move to pci.h */
 #define PCI_DEVICE_ID_SI_900   0x900   
 #define PCI_DEVICE_ID_SI_7016  0x7016  
 
index 9070dbff18bdeaeaaea53e844110aa1ed7d1a3ab..c918d013d0d19e3868a2831d604615be3b6247d0 100644 (file)
@@ -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 = 1518;
 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 <linux/config.h>
+#include <linux/version.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
-
-#include <asm/processor.h>             /* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <asm/processor.h>             /* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
 
-/* 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 <linux/version.h>             /* 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 <becker@cesdis.gsfc.nasa.gov>");
+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 <linux/delay.h>
-#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 <linux/shared_irq.h>
+#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 <linux/bios32.h>
+#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 @@ I. Board Compatibility
 
 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 @@ tx_full and tbusy flags.
 
 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);
 
-#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
+/* 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)
+
+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 @@ enum status_bits {
        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 @@ struct tulip_tx_desc {
        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 medialeaf {
 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 @@ struct tulip_private {
        /* 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 @@ struct tulip_private {
        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
 
 \f
 
-/* 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];
+
+               {
+#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
-               pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0,
-                                                                 &pci_ioaddr);
+                       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 @@ int tulip_probe(struct device *dev)
                                           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;
+
+       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 at %#3lx,",
-                  dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
+       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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
           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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
                        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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
                }
        }
        /* 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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
                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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
        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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
        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 @@ static struct device *tulip_probe1(int pci_bus, int pci_device_fn,
                        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;
 }
 \f
@@ -846,29 +1000,37 @@ static struct fixups {
 } 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 void parse_eeprom(struct device *dev)
        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 @@ static void parse_eeprom(struct device *dev)
                }
          }
          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 @@ subsequent_board:
        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;
-
-               media = get_u16(p);
-               p += 2;
-               count = *p++;
+               int media = get_u16(p);
+               int count = p[2];
+               p += 3;
 
-               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 @@ subsequent_board:
                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 @@ subsequent_board:
                                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 @@ subsequent_board:
                                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 @@ subsequent_board:
                                   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 @@ subsequent_board:
 /*  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 @@ static int mdio_read(struct device *dev, int phy_id, int location)
        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 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value)
        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 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value)
                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 @@ tulip_open(struct device *dev)
 {
        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 @@ tulip_open(struct device *dev)
 
        /* 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. */
-       {
-               u16 *eaddrs = (u16 *)dev->dev_addr;
-               u32 *setup_frm = tp->setup_frame, i;
-
-               /* 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];
+#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;
+               u16 *setup_frm = &tp->setup_frame[15*6];
+
+               /* 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 @@ tulip_open(struct device *dev)
        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 @@ tulip_open(struct device *dev)
                                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 @@ media_picked:
        /* 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 @@ static void select_media(struct device *dev, int startup)
        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 @@ static void select_media(struct device *dev, int startup)
                        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 @@ static void select_media(struct device *dev, int startup)
                        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 @@ static void select_media(struct device *dev, int startup)
                        }
                        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 @@ static void select_media(struct device *dev, int startup)
                        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 @@ static void select_media(struct device *dev, int startup)
                } 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 @@ static void select_media(struct device *dev, int startup)
                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 @@ static void select_media(struct device *dev, int startup)
        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 @@ static void tulip_timer(unsigned long data)
                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 @@ static void tulip_timer(unsigned long data)
                  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 @@ static void tulip_timer(unsigned long data)
                        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 @@ static void tulip_timer(unsigned long data)
        }
        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 @@ static void t21142_timer(unsigned long data)
        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 @@ static void t21142_timer(unsigned long data)
                        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));
-
-       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 {
+               printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, "
+                          "%8.8x.\n", dev->name, csr12, csr5, inl(ioaddr + CSR14));
+
+       /* 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 @@ static void t21142_lnk_change( struct device *dev)
                outl(tp->csr6 | 0x2002, ioaddr + CSR6);
        }
 }
-       
 
 static void mxic_timer(unsigned long data)
 {
@@ -1892,57 +2236,98 @@ static void mxic_timer(unsigned long data)
        }
 }
 
+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 @@ static void pnic_timer(unsigned long data)
                                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;
-
-  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);
+       struct tulip_private *tp = (struct tulip_private *)dev->priv;
+       long ioaddr = dev->base_addr;
 
-       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;
-       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");
+       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
 
-  /* 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;
+       /* 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 @@ tulip_init_ring(struct device *dev)
        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 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
        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 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                                }
 
                                /* 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 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                                /* 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 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
 
                /* 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 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                        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 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg
                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 @@ tulip_rx(struct device *dev)
        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 @@ tulip_rx(struct device *dev)
                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 @@ tulip_close(struct device *dev)
        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 @@ tulip_close(struct device *dev)
 #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 @@ static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 
        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 @@ static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd)
                }
                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 @@ static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd)
    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 @@ static inline unsigned ether_crc_le(int length, unsigned char *data)
        }
        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 @@ static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
 
        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 @@ static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
                                /* 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);
 }
 \f
 #ifdef CARDBUS
@@ -2745,18 +3157,18 @@ static void set_rx_mode(struct device *dev, int num_addrs, void *addrs)
 
 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 @@ static dev_node_t *tulip_attach(dev_locator_t *loc)
        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 @@ static void tulip_detach(dev_node_t *node)
 }
 
 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 */
 
 \f
 #ifdef MODULE
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-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 @@ init_module(void)
 #endif
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
        struct device *next_dev;
 
@@ -2832,9 +3265,11 @@ cleanup_module(void)
 
        /* 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 @@ cleanup_module(void)
 \f
 /*
  * 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
index 97e984fed5cd07ae789dc71a9a86ae0557270b89..cd5d8ef33a708de81b26d140145e5bb900030257 100644 (file)
@@ -308,7 +308,7 @@ static inline unsigned long get_timeout(idescsi_pc_t *pc)
 /*
  *     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 @@ static void idescsi_pc_intr (ide_drive_t *drive)
                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 @@ static void idescsi_pc_intr (ide_drive_t *drive)
                                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 @@ static void idescsi_pc_intr (ide_drive_t *drive)
        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 @@ static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
        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 @@ static void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned
 #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)
index eb612696595ce5baeef5ca04f88158b7f6ec5900..6ca462a804f9dca94d4368d54a250272d5bd3825 100644 (file)
@@ -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 <vandrove@vc.cvut.cz>
  *
- * Version: 1.15 1999/04/19
+ * Version: 1.19a 1999/08/12 (2.2.x branch)
  *
  * MTRR stuff: 1998 Tom Rini <tmrini@ntplx.net>
  *
@@ -14,8 +14,8 @@
  *               "Kurt Garloff" <garloff@kg1.ping.de>
  *                     Betatesting, fixes, ideas, videomodes, videomodes timmings
  *
- *               "Tom Rini" <tmrini@ntplx.net>
- *                     MTRR stuff, betatesting, fixes, ideas
+ *               "Tom Rini" <trini@disparity.net>
+ *                     MTRR stuff, PPC cleanups, betatesting, fixes, ideas
  *
  *               "Bibek Sahu" <scorpio@dodds.net>
  *                     Access device through readb|w|l and write b|w|l
@@ -60,6 +60,9 @@
  *               "Cort Dougan" <cort@cs.nmt.edu>
  *                     CHRP fixes and PReP cleanup
  *
+ *               "Mark Vojkovich" <mvojkovi@ucsd.edu>
+ *                     G400 support
+ *
  * (following author is not in any relation with this code, but his code
  *  is included in this driver)
  *
 /* 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 <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <video/macmodes.h>
 #endif
 
+/* always compile support for 32MB... It cost almost nothing */
+#define CONFIG_FB_MATROX_32MB
+
 #define FBCON_HAS_VGATEXT
 
 #ifdef MATROXFB_DEBUG
 
 #define DEBUG
-#define DBG(x)         printk("matroxfb: %s\n", (x));
+#define DBG(x)         printk(KERN_DEBUG "matroxfb: %s\n", (x));
 
 #ifdef MATROXFB_DEBUG_HEAVY
 #define DBG_HEAVY(x)   DBG(x)
 #ifndef PCI_DEVICE_ID_MATROX_G100_AGP
 #define PCI_DEVICE_ID_MATROX_G100_AGP  0x1001
 #endif
+#ifndef PCI_DEVICE_ID_MATROX_G400_AGP
+#define PCI_DEVICE_ID_MATROX_G400_AGP  0x0525
+#endif
 
 #ifndef PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP
 #define PCI_SS_ID_MATROX_GENERIC               0xFF00
@@ -422,7 +436,7 @@ struct matrox_hw_state {
        unsigned char   MiscOutReg;
        unsigned char   DACpal[768];
        unsigned char   CRTC[25];
-       unsigned char   CRTCEXT[6];
+       unsigned char   CRTCEXT[9];
        unsigned char   SEQ[5];
        /* unused for MGA mode, but who knows... */
        unsigned char   GCTL[9];
@@ -453,7 +467,8 @@ static inline struct matrox_fb_info* mxinfo(const struct display* p) {
 }
 
 #define PMXINFO(p) mxinfo(p),
-#define MINFO_FROM_DISP(x) struct matrox_fb_info* minfo = mxinfo(x)
+#define MINFO_FROM(x)     struct matrox_fb_info* minfo = x
+#define MINFO_FROM_DISP(x) MINFO_FROM(mxinfo(x))
 
 #else
 
@@ -476,6 +491,7 @@ static inline struct matrox_fb_info* mxinfo(const struct display* p) {
 #endif
 
 #define PMXINFO(p)
+#define MINFO_FROM(x)
 #define MINFO_FROM_DISP(x)
 
 #endif
@@ -563,6 +579,9 @@ struct matrox_fb_info {
                int             hwcursor;
                int             blink;
                int             sgram;
+#ifdef CONFIG_FB_MATROX_32MB
+               int             support32MB;
+#endif
 
                int             accelerator;
                int             text_type_aux;
@@ -609,6 +628,8 @@ struct matrox_fb_info {
 #if defined(CONFIG_FB_OF)
 unsigned char nvram_read_byte(int);
 int matrox_of_init(struct device_node *dp);
+static int default_vmode = VMODE_NVRAM;
+static int default_cmode = CMODE_NVRAM;
 #endif
 
 #define curr_ydstorg(x)        ACCESS_FBINFO2(x, curr.ydstorg.pixels)
@@ -678,6 +699,8 @@ int matrox_of_init(struct device_node *dp);
 
 #define M_RESET                0x1E40
 
+#define M_AGP2PLL      0x1E4C
+
 #define M_OPMODE       0x1E54
 #define     M_OPMODE_DMA_GEN_WRITE     0x00
 #define     M_OPMODE_DMA_BLIT          0x04
@@ -713,6 +736,9 @@ int matrox_of_init(struct device_node *dp);
 #define M_EXTVGA_INDEX 0x1FDE
 #define M_EXTVGA_DATA  0x1FDF
 
+/* G200 only */
+#define M_SRCORG       0x2CB4
+
 #define M_RAMDAC_BASE  0x3C00
 
 /* fortunately, same on TVP3026 and MGA1064 */
@@ -723,6 +749,9 @@ int matrox_of_init(struct device_node *dp);
 #define M_X_INDEX      0x00
 #define M_X_DATAREG    0x0A
 
+#define DAC_XGENIOCTRL         0x2A
+#define DAC_XGENIODATA         0x2B
+
 #ifdef CONFIG_FB_MATROX_MILLENIUM
 #define TVP3026_INDEX          0x00
 #define TVP3026_PALWRADD       0x00
@@ -1054,6 +1083,48 @@ int matrox_of_init(struct device_node *dp);
 #define isMilleniumII(x) (0)
 #endif
 
+#ifdef MATROXFB_DEBUG_REENTER
+static atomic_t guard_counter = ATOMIC_INIT(1);
+static atomic_t guard_printing = ATOMIC_INIT(1);
+static void guard_start(void) {
+       if (atomic_dec_and_test(&guard_counter)) {      /* first level */
+               if (!(bh_mask & (1 << CONSOLE_BH)))     /* and CONSOLE_BH disabled */
+                       return;                         /* is OK */
+               /* otherwise it is first level with CONSOLE_BH enabled -
+                  - if we are __sti or SMP, reentering from console_bh possible */
+               atomic_dec(&guard_printing);    /* disable reentrancy warning */
+               printk(KERN_DEBUG "matroxfb entered without CONSOLE_BH disabled\n");
+#ifdef printstate
+               printstate();
+#endif
+               atomic_inc(&guard_printing);
+               return;
+       }
+       /* real reentering... You should be already warned by code above */
+       if (atomic_dec_and_test(&guard_printing)) {
+#ifdef printstate
+               printstate();
+#endif
+       }
+       atomic_inc(&guard_printing);
+}
+
+static inline void guard_end(void) {
+       atomic_inc(&guard_counter);
+}
+
+#define CRITBEGIN guard_start();
+#define CRITEND   guard_end();
+
+#else
+
+#define CRITBEGIN
+#define CRITEND
+
+#endif
+
+#define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l))
+
 static void matrox_cfbX_init(WPMINFO struct display* p) {
        u_int32_t maccess;
        u_int32_t mpitch;
@@ -1101,7 +1172,7 @@ static void matrox_cfbX_init(WPMINFO struct display* p) {
        mga_outl(M_OPMODE, mopmode);
        mga_outl(M_CXBNDRY, 0xFFFF0000);
        mga_outl(M_YTOP, 0);
-       mga_outl(M_YBOT, 0x007FFFFF);
+       mga_outl(M_YBOT, 0x01FFFFFF);
        mga_outl(M_MACCESS, maccess);
        ACCESS_FBINFO(accel.m_dwg_rect) = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO;
        if (isMilleniumII(MINFO)) ACCESS_FBINFO(accel.m_dwg_rect) |= M_DWG_TRANSC;
@@ -1113,7 +1184,9 @@ static void matrox_cfbX_bmove(struct display* p, int sy, int sx, int dy, int dx,
        MINFO_FROM_DISP(p);
 
        DBG("matrox_cfbX_bmove")
-       
+
+       CRITBEGIN
+
        sx *= fontwidth(p);
        dx *= fontwidth(p);
        width *= fontwidth(p);
@@ -1142,8 +1215,10 @@ static void matrox_cfbX_bmove(struct display* p, int sy, int sx, int dy, int dx,
        mga_outl(M_AR0, end);
        mga_outl(M_AR3, start);
        mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
-       mga_outl(M_YDSTLEN | M_EXEC, ((dy)<<16) | height);
+       mga_ydstlen(dy, height);
        WaitTillIdle();
+
+       CRITEND
 }
 
 #ifdef FBCON_HAS_CFB4
@@ -1154,7 +1229,9 @@ static void matrox_cfb4_bmove(struct display* p, int sy, int sx, int dy, int dx,
           also odd, that means that we cannot use acceleration */
        
        DBG("matrox_cfb4_bmove")
-       
+
+       CRITBEGIN
+
        if ((sx | dx | width) & fontwidth(p) & 1) {
                fbcon_cfb4_bmove(p, sy, sx, dy, dx, height, width);
                return;
@@ -1194,6 +1271,8 @@ static void matrox_cfb4_bmove(struct display* p, int sy, int sx, int dy, int dx,
        mga_outl(M_YDST, dy*pixx >> 5);
        mga_outl(M_LEN | M_EXEC, height);
        WaitTillIdle();
+
+       CRITEND
 }
 #endif
 
@@ -1201,13 +1280,17 @@ static void matroxfb_accel_clear(CPMINFO u_int32_t color, int sy, int sx, int he
                int width) {
        
        DBG("matroxfb_accel_clear")
-       
-       mga_fifo(4);
+
+       CRITBEGIN
+
+       mga_fifo(5);
        mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_REPLACE);
        mga_outl(M_FCOL, color);
        mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
-       mga_outl(M_YDSTLEN | M_EXEC, (sy << 16) | height);
+       mga_ydstlen(sy, height);
        WaitTillIdle();
+
+       CRITEND
 }
 
 static void matrox_cfbX_clear(u_int32_t color, struct display* p, int sy, int sx, int height, int width) {
@@ -1225,7 +1308,9 @@ static void matrox_cfb4_clear(struct vc_data* conp, struct display* p, int sy, i
        MINFO_FROM_DISP(p);
 
        DBG("matrox_cfb4_clear")
-       
+
+       CRITBEGIN
+
        whattodo = 0; 
        bgx = attr_bgcol_ec(p, conp);
        bgx |= bgx << 4;
@@ -1277,6 +1362,8 @@ static void matrox_cfb4_clear(struct vc_data* conp, struct display* p, int sy, i
                        }
                }
        }
+
+       CRITEND
 }
 #endif
 
@@ -1323,7 +1410,10 @@ static void matrox_cfbX_fastputc(u_int32_t fgx, u_int32_t bgx, struct display* p
        charcell = fontwidth(p) * fontheight(p);
        yy *= fontheight(p);
        xx *= fontwidth(p);
-       mga_fifo(7);
+
+       CRITBEGIN
+
+       mga_fifo(8);
        mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
        
        mga_outl(M_FCOL, fgx);
@@ -1332,8 +1422,10 @@ static void matrox_cfbX_fastputc(u_int32_t fgx, u_int32_t bgx, struct display* p
        ar3 = ACCESS_FBINFO(fastfont.mgabase) + (c & p->charmask) * charcell;
        mga_outl(M_AR3, ar3);
        mga_outl(M_AR0, (ar3 + charcell - 1) & 0x0003FFFF);
-       mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+       mga_ydstlen(yy, fontheight(p));
        WaitTillIdle();
+
+       CRITEND
 }
        
 static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, int c, int yy, int xx) {
@@ -1345,6 +1437,9 @@ static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, in
        
        yy *= fontheight(p);
        xx *= fontwidth(p);
+
+       CRITBEGIN
+
 #ifdef __BIG_ENDIAN
        WaitTillIdle();
        mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1367,7 +1462,7 @@ static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, in
                mga_outl(M_BCOL, bgx);
                mga_outl(M_AR3, 0);
                mga_outl(M_AR0, fontheight(p)*fontwidth(p)-1);
-               mga_outl(M_YDSTLEN | M_EXEC, (yy<<16) | fontheight(p));
+               mga_ydstlen(yy, fontheight(p));
                mga_memcpy_toio(ACCESS_FBINFO(mmio.vbase), 0, p->fontdata+(c&p->charmask)*charcell, charcell);
        } else {
                u8* chardata = p->fontdata+(c&p->charmask)*fontheight(p)*step;
@@ -1379,7 +1474,7 @@ static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, in
                mga_outl(M_AR5, 0);
                mga_outl(M_AR3, 0);
                mga_outl(M_AR0, ar0);
-               mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+               mga_ydstlen(yy, fontheight(p));
 
                switch (step) {
                case 1: 
@@ -1410,6 +1505,7 @@ static void matrox_cfbX_putc(u_int32_t fgx, u_int32_t bgx, struct display* p, in
 #ifdef __BIG_ENDIAN
        mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+       CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1464,6 +1560,9 @@ static void matrox_cfbX_fastputcs(u_int32_t fgx, u_int32_t bgx, struct display*
        yy *= fontheight(p);
        xx *= fontwidth(p);
        charcell = fontwidth(p) * fontheight(p);
+
+       CRITBEGIN
+
        mga_fifo(3);
        mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
        mga_outl(M_FCOL, fgx);
@@ -1475,10 +1574,12 @@ static void matrox_cfbX_fastputcs(u_int32_t fgx, u_int32_t bgx, struct display*
                mga_outl(M_FXBNDRY, ((xx + fontwidth(p) - 1) << 16) | xx);
                mga_outl(M_AR3, ar3);
                mga_outl(M_AR0, (ar3 + charcell - 1) & 0x0003FFFF);
-               mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+               mga_ydstlen(yy, fontheight(p));
                xx += fontwidth(p);
        }
        WaitTillIdle();
+
+       CRITEND
 }
 
 static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, const unsigned short* s, int count, int yy, int xx) {
@@ -1504,7 +1605,7 @@ static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, c
                step = 4;
        charcell = fontheight(p)*step;
        xlen = (charcell + 3) & ~3;
-       ydstlen = (yy<<16) | fontheight(p);
+       ydstlen = (yy << 16) | fontheight(p);
        if (fontwidth(p) == step << 3) {
                ar0 = fontheight(p)*fontwidth(p) - 1;
                easy = 1;
@@ -1512,6 +1613,9 @@ static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, c
                ar0 = fontwidth(p) - 1;
                easy = 0;
        }
+
+       CRITBEGIN
+
 #ifdef __BIG_ENDIAN
        WaitTillIdle();
        mga_outl(M_OPMODE, M_OPMODE_8BPP);
@@ -1529,7 +1633,7 @@ static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, c
        while (count--) {
                u_int8_t* chardata = p->fontdata + (scr_readw(s++) & p->charmask)*charcell;
 
-               mga_fifo(5);
+               mga_fifo(6);
                mga_writel(mmio, M_FXBNDRY, fxbndry);
                mga_writel(mmio, M_AR0, ar0);
                mga_writel(mmio, M_AR3, 0);
@@ -1573,6 +1677,7 @@ static void matrox_cfbX_putcs(u_int32_t fgx, u_int32_t bgx, struct display* p, c
 #ifdef __BIG_ENDIAN
        mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+       CRITEND
 }
 
 #ifdef FBCON_HAS_CFB8
@@ -1635,6 +1740,8 @@ static void matrox_cfb4_revc(struct display* p, int xx, int yy) {
        xx |= (xx + fontwidth(p)) << 16;
        xx >>= 1;
 
+       CRITBEGIN
+       
        mga_fifo(5);
        mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
        mga_outl(M_FCOL, 0xFFFFFFFF);
@@ -1642,6 +1749,8 @@ static void matrox_cfb4_revc(struct display* p, int xx, int yy) {
        mga_outl(M_YDST, yy * p->var.xres_virtual >> 6);
        mga_outl(M_LEN | M_EXEC, fontheight(p));
        WaitTillIdle();
+
+       CRITEND
 } 
 #endif
 
@@ -1654,12 +1763,16 @@ static void matrox_cfb8_revc(struct display* p, int xx, int yy) {
        yy *= fontheight(p);
        xx *= fontwidth(p);
 
+       CRITBEGIN
+       
        mga_fifo(4);
        mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
        mga_outl(M_FCOL, 0x0F0F0F0F);
        mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
-       mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+       mga_ydstlen(yy, fontheight(p));
        WaitTillIdle();
+
+       CRITEND
 }
 #endif
 
@@ -1671,12 +1784,16 @@ static void matrox_cfbX_revc(struct display* p, int xx, int yy) {
        yy *= fontheight(p);
        xx *= fontwidth(p);
 
+       CRITBEGIN
+       
        mga_fifo(4);
        mga_outl(M_DWGCTL, ACCESS_FBINFO(accel.m_dwg_rect) | M_DWG_XOR);
        mga_outl(M_FCOL, 0xFFFFFFFF);
        mga_outl(M_FXBNDRY, ((xx + fontwidth(p)) << 16) | xx);
-       mga_outl(M_YDSTLEN | M_EXEC, (yy << 16) | fontheight(p));
+       mga_ydstlen(yy, fontheight(p));
        WaitTillIdle();
+
+       CRITEND
 }
 
 static void matrox_cfbX_clear_margins(struct vc_data* conp, struct display* p, int bottom_only) {
@@ -1840,6 +1957,9 @@ static void matroxfb_ti3026_cursor(struct display* p, int mode, int x, int y) {
 
        DBG("matroxfb_ti3026_cursor")
        
+       if (ACCESS_FBINFO(currcon_display) != p)
+               return;
+
        if (mode == CM_ERASE) {
                if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
                        spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC), flags);
@@ -1914,6 +2034,9 @@ static void matroxfb_DAC1064_createcursor(WPMINFO struct display* p) {
        xline = (~0) << (32 - ACCESS_FBINFO(cursor.w));
        cursorbase = ACCESS_FBINFO(video.vbase);
        h = ACCESS_FBINFO(features.DAC1064.cursorimage);
+
+       CRITBEGIN
+
 #ifdef __BIG_ENDIAN
        WaitTillIdle();
        mga_outl(M_OPMODE, M_OPMODE_32BPP);
@@ -1944,12 +2067,17 @@ static void matroxfb_DAC1064_createcursor(WPMINFO struct display* p) {
 #ifdef __BIG_ENDIAN
        mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
 #endif
+
+       CRITEND
 }
        
 static void matroxfb_DAC1064_cursor(struct display* p, int mode, int x, int y) {
        unsigned long flags;
        MINFO_FROM_DISP(p);
 
+       if (ACCESS_FBINFO(currcon_display) != p)
+               return;
+
        if (mode == CM_ERASE) {
                if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
                        spin_lock_irqsave(&ACCESS_FBINFO(lock.DAC), flags);
@@ -2010,6 +2138,9 @@ static int matroxfb_fastfont_tryset(WPMINFO struct display* p) {
        fsize = (p->userfont?FNTCHARCNT(p->fontdata):256) * fontheight(p);
        if (((fsize * width + 31) / 32) * 4 > ACCESS_FBINFO(fastfont.size))
                return 0;
+
+       CRITBEGIN
+
        mga_outl(M_OPMODE, M_OPMODE_8BPP);
        if (width <= 8) {
                if (width == 8)
@@ -2100,6 +2231,9 @@ static int matroxfb_fastfont_tryset(WPMINFO struct display* p) {
                }
        }
        mga_outl(M_OPMODE, ACCESS_FBINFO(accel.m_opmode));
+
+       CRITEND
+
        return 1;
 }
 
@@ -2117,6 +2251,8 @@ static void matrox_text_bmove(struct display* p, int sy, int sx, int dy, int dx,
        unsigned int step;
        MINFO_FROM_DISP(p);
 
+       CRITBEGIN
+
        step = ACCESS_FBINFO(devflags.textstep);
        srcoff = (sy * p->next_line) + (sx * step);
        dstoff = (dy * p->next_line) + (dx * step);
@@ -2144,6 +2280,7 @@ static void matrox_text_bmove(struct display* p, int sy, int sx, int dy, int dx,
                        height--;
                }
        }
+       CRITEND
 }
 
 static void matrox_text_clear(struct vc_data* conp, struct display* p, int sy, int sx,
@@ -2156,6 +2293,9 @@ static void matrox_text_clear(struct vc_data* conp, struct display* p, int sy, i
        step = ACCESS_FBINFO(devflags.textstep);
        offs = sy * p->next_line + sx * step;
        val = ntohs((attr_bgcol(p, conp->vc_video_erase_char) << 4) | attr_fgcol(p, conp->vc_video_erase_char) | (' ' << 8));
+
+       CRITBEGIN
+       
        while (height > 0) {
                int i;
                for (i = width; i > 0; offs += step, i--)
@@ -2163,6 +2303,7 @@ static void matrox_text_clear(struct vc_data* conp, struct display* p, int sy, i
                offs += p->next_line - width * step;
                height--;
        }
+       CRITEND
 }
 
 static void matrox_text_putc(struct vc_data* conp, struct display* p, int c, int yy, int xx) {
@@ -2175,7 +2316,12 @@ static void matrox_text_putc(struct vc_data* conp, struct display* p, int c, int
        offs = yy * p->next_line + xx * step;
        chr = attr_fgcol(p,c) | (attr_bgcol(p,c) << 4) | ((c & p->charmask) << 8);
        if (chr & 0x10000) chr |= 0x08;
+
+       CRITBEGIN
+       
        mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(chr));
+
+       CRITEND
 }
 
 static void matrox_text_putcs(struct vc_data* conp, struct display* p, const unsigned short* s,
@@ -2188,12 +2334,17 @@ static void matrox_text_putcs(struct vc_data* conp, struct display* p, const uns
        step = ACCESS_FBINFO(devflags.textstep);
        offs = yy * p->next_line + xx * step;
        attr = attr_fgcol(p, scr_readw(s)) | (attr_bgcol(p, scr_readw(s)) << 4);
+
+       CRITBEGIN
+       
        while (count-- > 0) {
                unsigned int chr = ((scr_readw(s++)) & p->charmask) << 8;
                if (chr & 0x10000) chr ^= 0x10008;
                mga_writew(ACCESS_FBINFO(video.vbase), offs, ntohs(attr|chr));
                offs += step;
        }
+
+       CRITEND
 }
 
 static void matrox_text_revc(struct display* p, int xx, int yy) {
@@ -2203,7 +2354,12 @@ static void matrox_text_revc(struct display* p, int xx, int yy) {
 
        step = ACCESS_FBINFO(devflags.textstep);
        offs = yy * p->next_line + xx * step + 1;
+       
+       CRITBEGIN
+       
        mga_writeb(ACCESS_FBINFO(video.vbase), offs, mga_readb(ACCESS_FBINFO(video.vbase), offs) ^ 0x77);
+
+       CRITEND
 }
 
 static int matrox_text_loadfont(WPMINFO struct display* p) {
@@ -2221,6 +2377,9 @@ static int matrox_text_loadfont(WPMINFO struct display* p) {
        dst = ACCESS_FBINFO(video.vbase);
        i = 2;
        font = (u_int8_t*)p->fontdata;
+
+       CRITBEGIN
+
        mga_setr(M_SEQ_INDEX, 0x02, 0x04);
        while (fsize--) {
                int l;
@@ -2233,6 +2392,9 @@ static int matrox_text_loadfont(WPMINFO struct display* p) {
                i += (32 - fontheight(p)) * ACCESS_FBINFO(devflags.vgastep);
        }
        mga_setr(M_SEQ_INDEX, 0x02, 0x03);
+
+       CRITEND
+
        return 1;
 }
 
@@ -2242,17 +2404,31 @@ static void matrox_text_createcursor(WPMINFO struct display* p) {
                return;
 
        matroxfb_createcursorshape(PMINFO p, 0);
+
+       CRITBEGIN
+       
        mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
        mga_setr(M_CRTC_INDEX, 0x0B, ACCESS_FBINFO(cursor.d) - 1);
+
+       CRITEND
 }
 
 static void matrox_text_cursor(struct display* p, int mode, int x, int y) {
        unsigned int pos;
        MINFO_FROM_DISP(p);
 
+       if (ACCESS_FBINFO(currcon_display) != p)
+               return;
+
        if (mode == CM_ERASE) {
                if (ACCESS_FBINFO(cursor.state) != CM_ERASE) {
+
+                       CRITBEGIN
+                       
                        mga_setr(M_CRTC_INDEX, 0x0A, 0x20);
+
+                       CRITEND
+
                        ACCESS_FBINFO(cursor.state) = CM_ERASE;
                }
                return;
@@ -2264,10 +2440,16 @@ static void matrox_text_cursor(struct display* p, int mode, int x, int y) {
        ACCESS_FBINFO(cursor.x) = x;
        ACCESS_FBINFO(cursor.y) = y;
        pos = p->next_line / ACCESS_FBINFO(devflags.textstep) * y + x;
+
+       CRITBEGIN
+       
        mga_setr(M_CRTC_INDEX, 0x0F, pos);
        mga_setr(M_CRTC_INDEX, 0x0E, pos >> 8);
 
        mga_setr(M_CRTC_INDEX, 0x0A, ACCESS_FBINFO(cursor.u));
+
+       CRITEND
+
        ACCESS_FBINFO(cursor.state) = CM_DRAW;
 }
 
@@ -2523,6 +2705,9 @@ static struct fb_var_screeninfo vesafb_defined __initdata = {
 static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) {
        unsigned int pos;
        unsigned short p0, p1, p2;
+#ifdef CONFIG_FB_MATROX_32MB
+       unsigned int p3;
+#endif
        struct display *disp;
 
        DBG("matrox_pan_var")
@@ -2536,10 +2721,22 @@ static void matrox_pan_var(WPMINFO struct fb_var_screeninfo *var) {
        }
        p0 = ACCESS_FBINFO(currenthw)->CRTC[0x0D] = pos & 0xFF;
        p1 = ACCESS_FBINFO(currenthw)->CRTC[0x0C] = (pos & 0xFF00) >> 8;
-       p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F);
+       p2 = ACCESS_FBINFO(currenthw)->CRTCEXT[0] = (ACCESS_FBINFO(currenthw)->CRTCEXT[0] & 0xB0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
+#ifdef CONFIG_FB_MATROX_32MB
+       p3 = ACCESS_FBINFO(currenthw)->CRTCEXT[8] = pos >> 21;
+#endif 
+
+       CRITBEGIN
+
        mga_setr(M_CRTC_INDEX, 0x0D, p0);
        mga_setr(M_CRTC_INDEX, 0x0C, p1);
+#ifdef CONFIG_FB_MATROX_32MB
+       if (ACCESS_FBINFO(devflags.support32MB))
+               mga_setr(M_EXTVGA_INDEX, 0x08, p3);
+#endif
        mga_setr(M_EXTVGA_INDEX, 0x00, p2);
+
+       CRITEND
 }
 
        /*
@@ -2627,13 +2824,16 @@ static int matroxfb_test_and_set_rounding(CPMINFO int xres, int bpp) {
                case 0:         return xres;
                case 4:         rounding = 128;
                                break;
-               case 8:         rounding = 64;
+               case 8:         rounding = 64;  /* doc says 64; 32 is OK for G400 */
                                break;
                case 16:        rounding = 32;
                                break;
-               case 24:        rounding = 64;
+               case 24:        rounding = 64;  /* doc says 64; 32 is OK for G400 */
                                break;
                default:        rounding = 16;
+                               /* on G400, 16 really does not work */
+                               if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG400)
+                                       rounding = 32;
                                break;
        }
        if (isInterleave(MINFO)) {
@@ -3140,7 +3340,7 @@ static void DAC1064_setpclk(CPMINFO struct matrox_hw_state* hw, unsigned long fo
        hw->DACclk[2] = p;
 }
 
-__initfunc(static void DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem)) {
+static void __init DAC1064_setmclk(CPMINFO struct matrox_hw_state* hw, int oscinfo, unsigned long fmem){
        u_int32_t mx;
 
        DBG("DAC1064_setmclk")
@@ -3288,6 +3488,8 @@ static int DAC1064_init_2(CPMINFO struct matrox_hw_state* hw, struct my_timming*
 static void DAC1064_restore_1(CPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw) {
        
        DBG("DAC1064_restore_1")
+
+       CRITBEGIN
        
        outDAC1064(PMINFO DAC1064_XSYSPLLM, hw->DACclk[3]);
        outDAC1064(PMINFO DAC1064_XSYSPLLN, hw->DACclk[4]);
@@ -3298,6 +3500,8 @@ static void DAC1064_restore_1(CPMINFO const struct matrox_hw_state* hw, const st
                for (i = 0; i < sizeof(MGA1064_DAC_regs); i++)
                        outDAC1064(PMINFO MGA1064_DAC_regs[i], hw->DACreg[i]);
        }
+
+       CRITEND
 }
 
 static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const struct matrox_hw_state* oldhw, struct display* p) {
@@ -3305,6 +3509,8 @@ static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const st
        unsigned int tmout;
        
        DBG("DAC1064_restore_2")
+
+       CRITBEGIN
        
        for (i = 0; i < 3; i++)
                outDAC1064(PMINFO M1064_XPIXPLLCM + i, hw->DACclk[i]);
@@ -3313,6 +3519,9 @@ static void DAC1064_restore_2(WPMINFO const struct matrox_hw_state* hw, const st
                        break;
                udelay(10);
        };
+
+       CRITEND
+
        if (!tmout)
                printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 
@@ -3585,7 +3794,9 @@ static int matroxfb_decode_var(CPMINFO struct display* p, struct fb_var_screenin
                        var->pixclock = 15000;  /* limit for "normal" gclk & mclk */
 #endif
        }
-
+       /* YDSTLEN contains only signed 16bit value */
+       if (var->yres_virtual > 32767)
+               var->yres_virtual = 32767;
        if (var->yres_virtual < var->yres)
                var->yres = var->yres_virtual;
        if (var->xres_virtual < var->xres)
@@ -3684,7 +3895,7 @@ static int matroxfb_decode_var(CPMINFO struct display* p, struct fb_var_screenin
 }
 
 #ifdef CONFIG_FB_MATROX_MILLENIUM
-__initfunc(static void ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout)) {
+static void __init ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fout){
        unsigned int f_pll;
        unsigned int pclk_m, pclk_n, pclk_p;
        unsigned int mclk_m, mclk_n, mclk_p;
@@ -3783,7 +3994,7 @@ __initfunc(static void ti3026_setMCLK(CPMINFO struct matrox_hw_state* hw, int fo
                printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
 }
 
-__initfunc(static void ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw)) {
+static void __init ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw){
 
        DBG("ti3026_ramdac_init")
 
@@ -3800,7 +4011,7 @@ __initfunc(static void ti3026_ramdac_init(WPMINFO struct matrox_hw_state* hw)) {
 }
 #endif
 
-__initfunc(static void matroxfb_fastfont_init(struct matrox_fb_info* minfo)) {
+static void matroxfb_fastfont_init(struct matrox_fb_info* minfo){
        unsigned int size;
 
        size = ACCESS_FBINFO(fastfont.size);
@@ -3824,7 +4035,7 @@ __initfunc(static void matroxfb_fastfont_init(struct matrox_fb_info* minfo)) {
 }
 
 #ifdef CONFIG_FB_MATROX_MYSTIQUE
-__initfunc(static void MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw){
 
        DBG("MGA1064_ramdac_init");
 
@@ -3841,7 +4052,7 @@ __initfunc(static void MGA1064_ramdac_init(WPMINFO struct matrox_hw_state* hw))
        DAC1064_setmclk(PMINFO hw, DAC1064_OPT_MDIV2 | DAC1064_OPT_GDIV3 | DAC1064_OPT_SCLK_PLL, 133333);
 }
 
-__initfunc(static int MGA1064_preinit(WPMINFO struct matrox_hw_state* hw)) {
+static int __init MGA1064_preinit(WPMINFO struct matrox_hw_state* hw){
        static const int vxres_mystique[] = { 512,        640, 768,  800,  832,  960, 
                                             1024, 1152, 1280,      1600, 1664, 1920, 
                                             2048,    0};
@@ -3873,7 +4084,7 @@ __initfunc(static int MGA1064_preinit(WPMINFO struct matrox_hw_state* hw)) {
        return 0;
 }
 
-__initfunc(static void MGA1064_reset(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGA1064_reset(WPMINFO struct matrox_hw_state* hw){
 
        DBG("MGA1064_reset");
 
@@ -3893,7 +4104,7 @@ static int x7AF4 = 0x10;    /* flags, maybe 0x10 = SDRAM, 0x00 = SGRAM??? */
 static int def50 = 0;  /* reg50, & 0x0F, & 0x3000 (only 0x0000, 0x1000, 0x2000 (0x3000 disallowed and treated as 0) */
 #endif
 
-__initfunc(static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p)) {
+static void __init MGAG100_progPixClock(CPMINFO int flags, int m, int n, int p){
        int reg;
        int selClk;
        int clk;
@@ -3937,7 +4148,7 @@ __initfunc(static void MGAG100_progPixClock(CPMINFO int flags, int m, int n, int
        outDAC1064(PMINFO M1064_XPIXCLKCTRL, inDAC1064(PMINFO M1064_XPIXCLKCTRL) & ~M1064_XPIXCLKCTRL_DIS);
 }
 
-__initfunc(static void MGAG100_setPixClock(CPMINFO int flags, int freq)) {
+static void __init MGAG100_setPixClock(CPMINFO int flags, int freq){
        unsigned int m, n, p;
 
        DBG("MGAG100_setPixClock")
@@ -3946,7 +4157,7 @@ __initfunc(static void MGAG100_setPixClock(CPMINFO int flags, int freq)) {
        MGAG100_progPixClock(PMINFO flags, m, n, p);
 }
 
-__initfunc(static int MGAG100_preinit(WPMINFO struct matrox_hw_state* hw)) {
+static int __init MGAG100_preinit(WPMINFO struct matrox_hw_state* hw){
        static const int vxres_g100[] = {  512,        640, 768,  800,  832,  960, 
                                           1024, 1152, 1280,      1600, 1664, 1920, 
                                           2048, 0};
@@ -4036,7 +4247,7 @@ __initfunc(static int MGAG100_preinit(WPMINFO struct matrox_hw_state* hw)) {
        return 0;
 }
        
-__initfunc(static void MGAG100_reset(WPMINFO struct matrox_hw_state* hw)) {
+static void __init MGAG100_reset(WPMINFO struct matrox_hw_state* hw){
        u_int8_t b;
 
        DBG("MGAG100_reset")
@@ -4104,6 +4315,8 @@ static void vgaHWrestore(CPMINFO struct matrox_hw_state* hw, struct matrox_hw_st
                dprintk("%02X:", hw->ATTR[i]);
        dprintk("\n");
 
+       CRITBEGIN
+
        mga_inb(M_ATTR_RESET);
        mga_outb(M_ATTR_INDEX, 0);
        mga_outb(M_MISC_REG, hw->MiscOutReg);
@@ -4125,6 +4338,8 @@ static void vgaHWrestore(CPMINFO struct matrox_hw_state* hw, struct matrox_hw_st
                mga_outb(M_DAC_VAL, hw->DACpal[i]);
        mga_inb(M_ATTR_RESET);
        mga_outb(M_ATTR_INDEX, 0x20);
+
+       CRITEND
 }
 
 static int matrox_setcolreg(unsigned regno, unsigned red, unsigned green,
@@ -4227,10 +4442,15 @@ static void MGA1064_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw
        int i;
 
        DBG("MGA1064_restore")
+
+       CRITBEGIN
        
        pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
        mga_outb(M_IEN, 0x00);
        mga_outb(M_CACHEFLUSH, 0x00);
+
+       CRITEND
+
        DAC1064_restore_1(PMINFO hw, oldhw);
        vgaHWrestore(PMINFO hw, oldhw);
        for (i = 0; i < 6; i++)
@@ -4244,10 +4464,18 @@ static void MGAG100_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw
        int i;
 
        DBG("MGAG100_restore")
-       
+
+       CRITBEGIN
+
        pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
+       CRITEND
+
        DAC1064_restore_1(PMINFO hw, oldhw);
        vgaHWrestore(PMINFO hw, oldhw);
+#ifdef CONFIG_FB_MATROX_32MB
+       if (ACCESS_FBINFO(devflags.support32MB))
+               mga_setr(M_EXTVGA_INDEX, 8, hw->CRTCEXT[8]);
+#endif
        for (i = 0; i < 6; i++)
                mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
        DAC1064_restore_2(PMINFO hw, oldhw, p);
@@ -4265,9 +4493,16 @@ static void Ti3026_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_
                dprintk("%02X:", hw->CRTCEXT[i]);
        dprintk("\n");
 
+       CRITBEGIN
+
        pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
 
+       CRITEND
+
        vgaHWrestore(PMINFO hw, oldhw);
+
+       CRITBEGIN
+
        for (i = 0; i < 6; i++)
                mga_setr(M_EXTVGA_INDEX, i, hw->CRTCEXT[i]);
 
@@ -4285,11 +4520,13 @@ static void Ti3026_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_
                oldhw->DACclk[2] = inTi3026(PMINFO TVP3026_XPIXPLLDATA);
                oldhw->DACclk[5] = inTi3026(PMINFO TVP3026_XLOOPPLLDATA);
        }
+       CRITEND
        if (!oldhw || memcmp(hw->DACclk, oldhw->DACclk, 6)) {
                /* agrhh... setting up PLL is very slow on Millenium... */
                /* Mystique PLL is locked in few ms, but Millenium PLL lock takes about 0.15 s... */
                /* Maybe even we should call schedule() ? */
 
+               CRITBEGIN
                outTi3026(PMINFO TVP3026_XCLKCTRL, hw->DACreg[POS3026_XCLKCTRL]);
                outTi3026(PMINFO TVP3026_XPLLADDR, 0x2A);
                outTi3026(PMINFO TVP3026_XLOOPPLLDATA, 0);
@@ -4307,24 +4544,31 @@ static void Ti3026_restore(WPMINFO struct matrox_hw_state* hw, struct matrox_hw_
                                        break;
                                udelay(10);
                        }
+
+                       CRITEND
+
                        if (!tmout)
                                printk(KERN_ERR "matroxfb: Pixel PLL not locked after 5 secs\n");
                        else
                                dprintk(KERN_INFO "PixelPLL: %d\n", 500000-tmout);
+                       CRITBEGIN
                }
                outTi3026(PMINFO TVP3026_XMEMPLLCTRL, hw->DACreg[POS3026_XMEMPLLCTRL]);
                outTi3026(PMINFO TVP3026_XPLLADDR, 0x00);
                for (i = 3; i < 6; i++)
                        outTi3026(PMINFO TVP3026_XLOOPPLLDATA, hw->DACclk[i]);
+               CRITEND
                if ((hw->MiscOutReg & 0x08) && ((hw->DACclk[5] & 0x80) == 0x80)) {
                        int tmout;
 
+                       CRITBEGIN
                        outTi3026(PMINFO TVP3026_XPLLADDR, 0x3F);
                        for (tmout = 500000; tmout; --tmout) {
                                if (inTi3026(PMINFO TVP3026_XLOOPPLLDATA) & 0x40) 
                                        break;
                                udelay(10);
                        }
+                       CRITEND
                        if (!tmout)
                                printk(KERN_ERR "matroxfb: Loop PLL not locked after 5 secs\n");
                        else
@@ -4379,7 +4623,7 @@ static int matroxfb_get_fix(struct fb_fix_screeninfo *fix, int con,
        strcpy(fix->id,"MATROX");
 
        fix->smem_start = (void*)ACCESS_FBINFO(video.base) + ACCESS_FBINFO(curr.ydstorg.bytes);
-       fix->smem_len = ACCESS_FBINFO(video.len) - ACCESS_FBINFO(curr.ydstorg.bytes);
+       fix->smem_len = ACCESS_FBINFO(video.len_usable) - ACCESS_FBINFO(curr.ydstorg.bytes);
        fix->type = p->type;
        fix->type_aux = p->type_aux;
        fix->visual = p->visual;
@@ -4531,7 +4775,8 @@ static int matroxfb_set_var(struct fb_var_screeninfo *var, int con,
 
                        hw->CRTC[0x0D] = pos & 0xFF;
                        hw->CRTC[0x0C] = (pos & 0xFF00) >> 8;
-                       hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F);
+                       hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
+                       hw->CRTCEXT[8] = pos >> 21;
                        ACCESS_FBINFO(hw_switch->restore(PMINFO hw, ohw, display));
                        ACCESS_FBINFO(cursor.redraw) = 1;
                        ACCESS_FBINFO(currenthw) = hw;
@@ -4717,10 +4962,16 @@ static void matroxfb_blank(int blank, struct fb_info *info)
                case 4:  seq = 0x20; crtc = 0x30; break;
                default: seq = 0x00; crtc = 0x00; break;
        }
+
+       CRITBEGIN
+       
        mga_outb(M_SEQ_INDEX, 1);
        mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
        mga_outb(M_EXTVGA_INDEX, 1);
        mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
+
+       CRITEND
+
 #undef minfo
 }
 
@@ -4739,7 +4990,8 @@ static void matroxfb_blank(int blank, struct fb_info *info)
 #define RS1056x344     12      /* 132 x 43 text */
 #define RS1056x400     13      /* 132 x 50 text */
 #define RS1056x480     14      /* 132 x 60 text */
-/* 0F-FF */
+#define RSNoxNo                2       /* FIXME! change it to 15 after modedb patch (2.3 only) */
+/* 10-FF */
 static struct { int xres, yres, left, right, upper, lower, hslen, vslen, vfreq; } timmings[] __initdata = {
        {  640,  400,  48, 16, 39,  8,  96, 2, 70 },
        {  640,  480,  48, 16, 33, 10,  96, 2, 60 },
@@ -4754,7 +5006,8 @@ static struct { int xres, yres, left, right, upper, lower, hslen, vslen, vfreq;
        {  640,  350,  48, 16, 39,  8,  96, 2, 70 },
        { 1056,  344,  96, 24, 59, 44, 160, 2, 70 },
        { 1056,  400,  96, 24, 39,  8, 160, 2, 70 },
-       { 1056,  480,  96, 24, 36, 12, 160, 3, 60 }
+       { 1056,  480,  96, 24, 36, 12, 160, 3, 60 },
+       {    0,    0,  ~0, ~0, ~0, ~0,   0, 0,  0 }
 };
 
 #define RSDepth(X)     (((X) >> 8) & 0x0F)
@@ -4775,13 +5028,14 @@ static struct { struct fb_bitfield red, green, blue, transp; int bits_per_pixel;
        { {  0, 8, 0}, { 0, 8, 0}, { 0, 8, 0}, {  0, 0, 0},  4 },
        { { 16, 8, 0}, { 8, 8, 0}, { 0, 8, 0}, {  0, 0, 0}, 24 },
        { {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 },       /* textmode with (default) VGA8x16 */
-       { {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 }        /* textmode hardwired to VGA8x8 */
+       { {  0, 6, 0}, { 0, 6, 0}, { 0, 6, 0}, {  0, 0, 0},  0 },       /* textmode hardwired to VGA8x8 */
 };
 
 #define RSCreate(X,Y)  ((X) | ((Y) << 8))
 static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __initdata = {
 /* default must be first */
 #ifdef FBCON_HAS_CFB8
+       {    ~0, RSCreate(RSNoxNo,     RS8bpp ) },
        { 0x101, RSCreate(RS640x480,   RS8bpp ) },
        { 0x100, RSCreate(RS640x400,   RS8bpp ) },
        { 0x180, RSCreate(RS768x576,   RS8bpp ) },
@@ -4793,14 +5047,8 @@ static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __init
        { 0x198, RSCreate(RS1408x1056, RS8bpp ) },
        { 0x11C, RSCreate(RS1600x1200, RS8bpp ) },
 #endif
-#ifdef FBCON_HAS_CFB4
-       { 0x010, RSCreate(RS640x350,   RS4bpp ) },
-       { 0x012, RSCreate(RS640x480,   RS4bpp ) },
-       { 0x102, RSCreate(RS800x600,   RS4bpp ) },
-       { 0x104, RSCreate(RS1024x768,  RS4bpp ) },
-       { 0x106, RSCreate(RS1280x1024, RS4bpp ) },
-#endif
 #ifdef FBCON_HAS_CFB16
+       {    ~0, RSCreate(RSNoxNo,     RS15bpp) },
        { 0x110, RSCreate(RS640x480,   RS15bpp) },
        { 0x181, RSCreate(RS768x576,   RS15bpp) },
        { 0x113, RSCreate(RS800x600,   RS15bpp) },
@@ -4821,6 +5069,7 @@ static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __init
        { 0x11E, RSCreate(RS1600x1200, RS16bpp) },
 #endif
 #ifdef FBCON_HAS_CFB24
+       {    ~0, RSCreate(RSNoxNo,     RS24bpp) },
        { 0x1B2, RSCreate(RS640x480,   RS24bpp) },
        { 0x184, RSCreate(RS768x576,   RS24bpp) },
        { 0x1B5, RSCreate(RS800x600,   RS24bpp) },
@@ -4832,6 +5081,7 @@ static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __init
        { 0x1BF, RSCreate(RS1600x1200, RS24bpp) },
 #endif
 #ifdef FBCON_HAS_CFB32
+       {    ~0, RSCreate(RSNoxNo,     RS32bpp) },
        { 0x112, RSCreate(RS640x480,   RS32bpp) },
        { 0x183, RSCreate(RS768x576,   RS32bpp) },
        { 0x115, RSCreate(RS800x600,   RS32bpp) },
@@ -4843,6 +5093,7 @@ static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __init
        { 0x11F, RSCreate(RS1600x1200, RS32bpp) },
 #endif
 #ifdef FBCON_HAS_VGATEXT
+       {    ~0, RSCreate(RSNoxNo,     RSText)  },
        { 0x002, RSCreate(RS640x400,   RSText)  },      /* 80x25 */
        { 0x003, RSCreate(RS640x400,   RSText)  },      /* 80x25 */
        { 0x007, RSCreate(RS640x400,   RSText)  },      /* 80x25 */
@@ -4852,6 +5103,14 @@ static struct { unsigned int vesa; unsigned int info; } *RSptr, vesamap[] __init
        { 0x10A, RSCreate(RS1056x344,  RSText8) },      /* 132x43 */
        { 0x10B, RSCreate(RS1056x400,  RSText8) },      /* 132x50 */
        { 0x10C, RSCreate(RS1056x480,  RSText8) },      /* 132x60 */
+#endif
+#ifdef FBCON_HAS_CFB4
+       {    ~0, RSCreate(RSNoxNo,     RS4bpp ) },
+       { 0x010, RSCreate(RS640x350,   RS4bpp ) },
+       { 0x012, RSCreate(RS640x480,   RS4bpp ) },
+       { 0x102, RSCreate(RS800x600,   RS4bpp ) },
+       { 0x104, RSCreate(RS1024x768,  RS4bpp ) },
+       { 0x106, RSCreate(RS1280x1024, RS4bpp ) },
 #endif
        {     0, 0                                 }};
 
@@ -4871,19 +5130,21 @@ static int inverse = 0;                 /* "matrox:inverse" */
 static int hwcursor = 1;               /* "matrox:nohwcursor" */
 static int blink = 1;                  /* "matrox:noblink" */
 static int sgram = 0;                  /* "matrox:sgram" */
+#ifdef CONFIG_MTRR
 static int mtrr = 1;                   /* "matrox:nomtrr" */
+#endif
 static int grayscale = 0;              /* "matrox:grayscale" */
 static unsigned int fastfont = 0;      /* "matrox:fastfont:xxxxx" */
 static int dev = -1;                   /* "matrox:dev:xxxxx" */
-static unsigned int vesa = 0x101;      /* "matrox:vesa:xxxxx" */
+static unsigned int vesa = ~0;         /* "matrox:vesa:xxxxx" */
 static int depth = -1;                 /* "matrox:depth:xxxxx" */
 static unsigned int xres = 0;          /* "matrox:xres:xxxxx" */
 static unsigned int yres = 0;          /* "matrox:yres:xxxxx" */
-static unsigned int upper = 0;         /* "matrox:upper:xxxxx" */
-static unsigned int lower = 0;         /* "matrox:lower:xxxxx" */
+static unsigned int upper = ~0;                /* "matrox:upper:xxxxx" */
+static unsigned int lower = ~0;                /* "matrox:lower:xxxxx" */
 static unsigned int vslen = 0;         /* "matrox:vslen:xxxxx" */
-static unsigned int left = 0;          /* "matrox:left:xxxxx" */
-static unsigned int right = 0;         /* "matrox:right:xxxxx" */
+static unsigned int left = ~0;         /* "matrox:left:xxxxx" */
+static unsigned int right = ~0;                /* "matrox:right:xxxxx" */
 static unsigned int hslen = 0;         /* "matrox:hslen:xxxxx" */
 static unsigned int pixclock = 0;      /* "matrox:pixclock:xxxxx" */
 static int sync = -1;                  /* "matrox:sync:xxxxx" */
@@ -4891,6 +5152,7 @@ static unsigned int fv = 0;               /* "matrox:fv:xxxxx" */
 static unsigned int fh = 0;            /* "matrox:fh:xxxxxk" */
 static unsigned int maxclk = 0;                /* "matrox:maxclk:xxxxM" */
 static char fontname[64];              /* "matrox:font:xxxxx" */
+static char videomode[64];             /* "matrox:mode:xxxxx" or "matrox:xxxxx" */
 
 #ifndef MODULE
 __initfunc(void matroxfb_setup(char *options, int *ints)) {
@@ -4954,6 +5216,31 @@ __initfunc(void matroxfb_setup(char *options, int *ints)) {
                        fv = simple_strtoul(this_opt+3, NULL, 0);
                else if (!strncmp(this_opt, "mem:", 4)) 
                        mem = simple_strtoul(this_opt+4, NULL, 0);
+               else if (!strncmp(this_opt, "mode:", 5))
+                       strcpy(videomode, this_opt+5);
+#ifdef CONFIG_FB_OF
+               else if (!strncmp(this_opt, "vmode:", 6)) {
+                       unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
+                       if (vmode > 0 && vmode <= VMODE_MAX)
+                               default_vmode = vmode;
+               } else if (!strncmp(this_opt, "cmode:", 6)) {
+                       unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
+                       switch (cmode) {
+                               case 0:
+                               case 8:
+                                       default_cmode = CMODE_8;
+                                       break;
+                               case 15:
+                               case 16:
+                                       default_cmode = CMODE_16;
+                                       break;
+                               case 24:
+                               case 32:
+                                       default_cmode = CMODE_32;
+                                       break;
+                       }
+               }
+#endif
                else if (!strncmp(this_opt, "fastfont:", 9))
                        fastfont = simple_strtoul(this_opt+9, NULL, 0);
                else if (!strcmp(this_opt, "nofastfont"))       /* fastfont:N and nofastfont (nofastfont = fastfont:0) */
@@ -4987,8 +5274,10 @@ __initfunc(void matroxfb_setup(char *options, int *ints)) {
                                nobios = !value;
                        else if (!strcmp(this_opt, "init"))
                                noinit = !value;
+#ifdef CONFIG_MTRR
                        else if (!strcmp(this_opt, "mtrr"))
                                mtrr = value;
+#endif
                        else if (!strcmp(this_opt, "inv24"))
                                inv24 = value;
                        else if (!strcmp(this_opt, "cross4MB"))
@@ -5004,6 +5293,7 @@ __initfunc(void matroxfb_setup(char *options, int *ints)) {
                        }
                }
        }
+       return;
 }
 #endif
 
@@ -5012,7 +5302,7 @@ __initfunc(static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned
        unsigned int offs;
        unsigned int offs2;
        unsigned char store;
-       unsigned char bytes[16];
+       unsigned char bytes[32];
        unsigned char* tmp;
        unsigned long cbase;
        unsigned long mbase;
@@ -5025,7 +5315,7 @@ __initfunc(static int matroxfb_getmemory(WPMINFO unsigned int maxSize, unsigned
        maxSize &= ~0x1FFFFF;   /* must be X*2MB (really it must be 2 or X*4MB) */
        /* at least 2MB */
        if (maxSize < 0x0200000) return 0;
-       if (maxSize > 0x1000000) maxSize = 0x1000000;
+       if (maxSize > 0x2000000) maxSize = 0x2000000;
 
        mga_outb(M_EXTVGA_INDEX, 0x03);
        mga_outb(M_EXTVGA_DATA, mga_inb(M_EXTVGA_DATA) | 0x80);
@@ -5163,20 +5453,28 @@ static struct matrox_switch matrox_G100 = {
 
 struct video_board {
        int maxvram;
+       int maxdisplayable;
        int accelID;
        struct matrox_switch* lowlevel;
                 };
 #ifdef CONFIG_FB_MATROX_MILLENIUM
-static struct video_board vbMillenium __initdata       = {0x0800000,   FB_ACCEL_MATROX_MGA2064W,       &matrox_millenium};
-static struct video_board vbMillenium2 __initdata      = {0x1000000,   FB_ACCEL_MATROX_MGA2164W,       &matrox_millenium};
-static struct video_board vbMillenium2A __initdata     = {0x1000000,   FB_ACCEL_MATROX_MGA2164W_AGP,   &matrox_millenium};
+static struct video_board vbMillenium __initdata   = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA2064W,    &matrox_millenium};
+static struct video_board vbMillenium2 __initdata  = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W,    &matrox_millenium};
+static struct video_board vbMillenium2A __initdata = {0x1000000, 0x0800000, FB_ACCEL_MATROX_MGA2164W_AGP, &matrox_millenium};
 #endif /* CONFIG_FB_MATROX_MILLENIUM */
 #ifdef CONFIG_FB_MATROX_MYSTIQUE
-static struct video_board vbMystique __initdata                = {0x0800000,   FB_ACCEL_MATROX_MGA1064SG,      &matrox_mystique};
+static struct video_board vbMystique __initdata    = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGA1064SG,   &matrox_mystique};
 #endif /* CONFIG_FB_MATROX_MYSTIQUE */
 #ifdef CONFIG_FB_MATROX_G100
-static struct video_board vbG100 __initdata            = {0x0800000,   FB_ACCEL_MATROX_MGAG100,        &matrox_G100};
-static struct video_board vbG200 __initdata            = {0x1000000,   FB_ACCEL_MATROX_MGAG200,        &matrox_G100};
+static struct video_board vbG100 __initdata       = {0x0800000, 0x0800000, FB_ACCEL_MATROX_MGAG100,      &matrox_G100};
+static struct video_board vbG200 __initdata       = {0x1000000, 0x1000000, FB_ACCEL_MATROX_MGAG200,      &matrox_G100};
+#ifdef CONFIG_FB_MATROX_32MB
+/* from doc it looks like that accelerator can draw only to low 16MB :-( Direct accesses & displaying are OK for
+   whole 32MB */
+static struct video_board vbG400 __initdata       = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400,      &matrox_G100};
+#else
+static struct video_board vbG400 __initdata       = {0x2000000, 0x1000000, FB_ACCEL_MATROX_MGAG400,      &matrox_G100};
+#endif
 #endif
 
 #define DEVF_VIDEO64BIT        0x01
@@ -5185,6 +5483,15 @@ static struct video_board vbG200 __initdata              = {0x1000000,   FB_ACCEL_MATROX_MGAG2
 #define        DEVF_MILLENIUM2 0x08
 #define DEVF_CROSS4MB  0x10
 #define DEVF_TEXT4B    0x20
+#define DEVF_DDC_8_2   0x40
+#define DEVF_DMA       0x80
+#define DEVF_SUPPORT32MB       0x100
+#define DEVF_ANY_VXRES         0x200
+
+#define DEVF_G100      (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2) /* no doc, no vxres... */
+#define DEVF_G200      (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2 | DEVF_ANY_VXRES)
+#define DEVF_G400      (DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2 | DEVF_ANY_VXRES | DEVF_SUPPORT32MB)
+
 static struct board {
        unsigned short vendor, device, rev, svid, sid;
        unsigned int flags;
@@ -5229,88 +5536,94 @@ static struct board {
 #ifdef CONFIG_FB_MATROX_G100
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100,      0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_MGA_G100_PCI,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "MGA-G100 (PCI)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100,      0xFF,
                0,                      0,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "unknown G100 (PCI)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_GENERIC,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "MGA-G100 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_MGA_G100_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "MGA-G100 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100_AGP,  0xFF,
                PCI_SS_VENDOR_ID_SIEMENS_NIXDORF,       PCI_SS_ID_SIEMENS_MGA_G100_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "MGA-G100 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_PRODUCTIVA_G100_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "Productiva G100 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G100_AGP,  0xFF,
                0,                      0,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G100,
                230000,
                &vbG100,
                "unknown G100 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_PCI,  0xFF,
                0,                      0,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                250000,
                &vbG200,
                "unknown G200 (PCI)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_GENERIC,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                220000,
                &vbG200,
                "MGA-G200 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_MYSTIQUE_G200_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                230000,
                &vbG200,
                "Mystique G200 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_MILLENIUM_G200_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                250000,
                &vbG200,
                "Millennium G200 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                PCI_SS_VENDOR_ID_MATROX,        PCI_SS_ID_MATROX_MARVEL_G200_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                230000,
                &vbG200,
                "Marvel G200 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                PCI_SS_VENDOR_ID_SIEMENS_NIXDORF,       PCI_SS_ID_SIEMENS_MGA_G200_AGP,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                230000,
                &vbG200,
                "MGA-G200 (AGP)"},
        {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G200_AGP,  0xFF,
                0,                      0,
-               DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB,
+               DEVF_G200,
                230000,
                &vbG200,
                "unknown G200 (AGP)"},
+       {PCI_VENDOR_ID_MATROX,  PCI_DEVICE_ID_MATROX_G400_AGP,  0xFF,
+               0,                      0,
+               DEVF_G400,
+               360000,
+               &vbG400,
+               "unknown G400 (AGP)"},
 #endif
        {0,                     0,                              0xFF,
                0,                      0,
@@ -5348,6 +5661,10 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
                ACCESS_FBINFO(devflags.vgastepdisp) = 64;
                ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP8;
        }
+#ifdef CONFIG_FB_MATROX_32MB
+       ACCESS_FBINFO(devflags.support32MB) = b->flags & DEVF_SUPPORT32MB;
+#endif
+       ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES);
        ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode);
        ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode);
 
@@ -5472,8 +5789,8 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
                return -ENOMEM;
        }
        ACCESS_FBINFO(video.len_usable) = ACCESS_FBINFO(video.len);
-       if (ACCESS_FBINFO(video.len_usable) > 0x08000000)
-               ACCESS_FBINFO(video.len_usable) = 0x08000000;
+       if (ACCESS_FBINFO(video.len_usable) > b->base->maxdisplayable)
+               ACCESS_FBINFO(video.len_usable) = b->base->maxdisplayable;
 #ifdef CONFIG_MTRR
        if (mtrr) {
                ACCESS_FBINFO(mtrr.vram) = mtrr_add(video_base_phys, ACCESS_FBINFO(video.len), MTRR_TYPE_WRCOMB, 1);
@@ -5482,11 +5799,16 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
        }
 #endif /* CONFIG_MTRR */
 
+       if (!ACCESS_FBINFO(devflags.novga))
+               request_region(0x3C0, 32, "matrox");
+       ACCESS_FBINFO(hw_switch->reset(PMINFO hw));
+
 /* validate params, autodetect k, M */
        if (fh < 1000) fh *= 1000;      /* 1kHz minimum */
        if (maxclk < 1000) maxclk *= 1000;      /* kHz -> Hz, MHz -> kHz */
        if (maxclk < 1000000) maxclk *= 1000;   /* kHz -> Hz, 1MHz minimum */
-       vesa &= 0x1DFF;         /* mask out clearscreen, acceleration and so on */
+       if (vesa != ~0)
+               vesa &= 0x1DFF;         /* mask out clearscreen, acceleration and so on */
 
        ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0;
        ACCESS_FBINFO(fbcon.monspecs.hfmax) = fh;
@@ -5504,19 +5826,19 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
        }
        {
                int res = RSResolution(RSptr->info)-1;
-               if (!left)
+               if (left == ~0)
                        left = timmings[res].left;
                if (!xres)
                        xres = timmings[res].xres;
-               if (!right)
+               if (right == ~0)
                        right = timmings[res].right;
                if (!hslen)
                        hslen = timmings[res].hslen;
-               if (!upper)
+               if (upper == ~0)
                        upper = timmings[res].upper;
                if (!yres)
                        yres = timmings[res].yres;
-               if (!lower)
+               if (lower == ~0)
                        lower = timmings[res].lower;
                if (!vslen)
                        vslen = timmings[res].vslen;
@@ -5525,38 +5847,6 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
                if (depth == -1)
                        depth = RSDepth(RSptr->info);
        }
-       if (sync == -1) {
-               sync = 0;
-               if (yres < 400)
-                       sync |= FB_SYNC_HOR_HIGH_ACT;
-               else if (yres < 480)
-                       sync |= FB_SYNC_VERT_HIGH_ACT;
-       }
-       if (xres < 320)
-               xres = 320;
-       if (xres > 2048)
-               xres = 2048;
-       if (yres < 200)
-               yres = 200;
-       if (yres > 2048)
-               yres = 2048;
-       {
-               unsigned int tmp;
-
-               if (fv) {
-                       tmp = fv * (upper + yres + lower + vslen);
-                       if ((tmp < fh) || (fh == 0)) fh = tmp;
-               }
-               if (fh) {
-                       tmp = fh * (left + xres + right + hslen);
-                       if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp;
-               }
-               maxclk = (maxclk + 499) / 500;
-               if (maxclk) {
-                       tmp = (2000000000 + maxclk) / maxclk;
-                       if (tmp > pixclock) pixclock = tmp;
-               }
-       }
        if ((depth == RSText8) && (!*ACCESS_FBINFO(fbcon.fontname))) {
                strcpy(ACCESS_FBINFO(fbcon.fontname), "VGA8x8");
        }
@@ -5564,27 +5854,9 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
        vesafb_defined.green = colors[depth-1].green;
        vesafb_defined.blue = colors[depth-1].blue;
        vesafb_defined.bits_per_pixel = colors[depth-1].bits_per_pixel;
-       if (pixclock < 2000)            /* > 500MHz */
-               pixclock = 4000;        /* 250MHz */
-       if (pixclock > 1000000)
-               pixclock = 1000000;     /* 1MHz */
-       vesafb_defined.xres = xres;
-       vesafb_defined.yres = yres;
-       vesafb_defined.xoffset = 0;
-       vesafb_defined.yoffset = 0;
        vesafb_defined.grayscale = grayscale;
-       vesafb_defined.pixclock = pixclock;
-       vesafb_defined.left_margin = left;
-       vesafb_defined.right_margin = right;
-       vesafb_defined.hsync_len = hslen;
-       vesafb_defined.upper_margin = upper;
-       vesafb_defined.lower_margin = lower;
-       vesafb_defined.vsync_len = vslen;
-       vesafb_defined.sync = sync;
        vesafb_defined.vmode = 0;
-
-       if (!ACCESS_FBINFO(devflags.novga))
-               request_region(0x3C0, 32, "matrox");
+       vesafb_defined.sync = -1;
        if (noaccel)
                vesafb_defined.accel_flags &= ~FB_ACCELF_TEXT;
 
@@ -5597,8 +5869,67 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
        ACCESS_FBINFO(fbcon.updatevar) = &matroxfb_updatevar;
        ACCESS_FBINFO(fbcon.blank) = &matroxfb_blank;
        ACCESS_FBINFO(fbcon.flags) = FBINFO_FLAG_DEFAULT;
-       ACCESS_FBINFO(hw_switch->reset(PMINFO hw));
        ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
+
+#if 0
+       fb_find_mode(&vesafb_defined, &ACCESS_FBINFO(fbcon), videomode[0]?videomode:NULL,
+               NULL, 0, NULL, vesafb_defined.bits_per_pixel);
+#endif
+       /* mode modifiers */
+       if (hslen)
+               vesafb_defined.hsync_len = hslen;
+       if (vslen)
+               vesafb_defined.vsync_len = vslen;
+       if (left != ~0)
+               vesafb_defined.left_margin = left;
+       if (right != ~0)
+               vesafb_defined.right_margin = right;
+       if (upper != ~0)
+               vesafb_defined.upper_margin = upper;
+       if (lower != ~0)
+               vesafb_defined.lower_margin = lower;
+       if (xres)
+               vesafb_defined.xres = xres;
+       if (yres)
+               vesafb_defined.yres = yres;
+       if (sync != -1)
+               vesafb_defined.sync = sync;
+       else if (vesafb_defined.sync == -1) {
+               vesafb_defined.sync = 0;
+               if (vesafb_defined.yres < 400)
+                       vesafb_defined.sync |= FB_SYNC_HOR_HIGH_ACT;
+               else if (vesafb_defined.yres < 480)
+                       vesafb_defined.sync |= FB_SYNC_VERT_HIGH_ACT;
+       }
+       /* fv, fh, maxclk limits was specified */
+       {
+               unsigned int tmp;
+
+               if (fv) {
+                       tmp = fv * (vesafb_defined.upper_margin + vesafb_defined.yres 
+                                 + vesafb_defined.lower_margin + vesafb_defined.vsync_len);
+                       if ((tmp < fh) || (fh == 0)) fh = tmp;
+               }
+               if (fh) {
+                       tmp = fh * (vesafb_defined.left_margin + vesafb_defined.xres 
+                                 + vesafb_defined.right_margin + vesafb_defined.hsync_len);
+                       if ((tmp < maxclk) || (maxclk == 0)) maxclk = tmp;
+               }
+               maxclk = (maxclk + 499) / 500;
+               if (maxclk) {
+                       tmp = (2000000000 + maxclk) / maxclk;
+                       if (tmp > pixclock) pixclock = tmp;
+               }
+       }
+       if (pixclock) {
+               if (pixclock < 2000)            /* > 500MHz */
+                       pixclock = 4000;        /* 250MHz */
+               if (pixclock > 1000000)
+                       pixclock = 1000000;     /* 1MHz */
+               vesafb_defined.pixclock = pixclock;
+       }
+               
+       /* FIXME: Where to move this?! */
 #if defined(CONFIG_FB_OF)
 #if defined(CONFIG_FB_COMPAT_XPMAC)
        strcpy(ACCESS_FBINFO(matrox_name), "MTRX,");    /* OpenFirmware naming convension */
@@ -5608,30 +5939,30 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
 #endif
        if ((xres <= 640) && (yres <= 480)) {
                struct fb_var_screeninfo var;
-               int default_vmode = nvram_read_byte(NV_VMODE);
-               int default_cmode = nvram_read_byte(NV_CMODE);
-
-               if ((default_vmode <= 0) || (default_vmode > VMODE_MAX))
+               if (default_vmode == VMODE_NVRAM) {
+                       default_vmode = nvram_read_byte(NV_VMODE);
+                       if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+                               default_vmode = VMODE_CHOOSE;
+               }
+               if (default_vmode <= 0 || default_vmode > VMODE_MAX)
                        default_vmode = VMODE_640_480_60;
-               if ((default_cmode < CMODE_8) || (default_cmode > CMODE_32))
+               if (default_cmode == CMODE_NVRAM)
+                       default_cmode = nvram_read_byte(NV_CMODE);
+               if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
                        default_cmode = CMODE_8;
                if (!mac_vmode_to_var(default_vmode, default_cmode, &var)) {
                        var.accel_flags = vesafb_defined.accel_flags;
                        var.xoffset = var.yoffset = 0;
                        vesafb_defined = var; /* Note: mac_vmode_to_var() doesnot set all parameters */
-               }
+              }
        }
 #endif
-       {
-               int pixel_size = vesafb_defined.bits_per_pixel;
-
-               vesafb_defined.xres_virtual = matroxfb_pitch_adjust(PMINFO vesafb_defined.xres, pixel_size);
-               if (nopan) {
-                       vesafb_defined.yres_virtual = vesafb_defined.yres;
-               } else {
-                       vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough
-                                                               to yres_virtual * xres_virtual < 2^32 */
-               }
+       vesafb_defined.xres_virtual = vesafb_defined.xres;
+       if (nopan) {
+               vesafb_defined.yres_virtual = vesafb_defined.yres;
+       } else {
+               vesafb_defined.yres_virtual = 65536; /* large enough to be INF, but small enough
+                                                       to yres_virtual * xres_virtual < 2^32 */
        }
        if (matroxfb_set_var(&vesafb_defined, -2, &ACCESS_FBINFO(fbcon))) {
                printk(KERN_ERR "matroxfb: cannot set required parameters\n");
@@ -5647,8 +5978,9 @@ __initfunc(static int initMatrox2(WPMINFO struct display* d, struct board* b)) {
 /* We do not have to set currcon to 0... register_framebuffer do it for us on first console
  * and we do not want currcon == 0 for subsequent framebuffers */
 
-       if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0)
+       if (register_framebuffer(&ACCESS_FBINFO(fbcon)) < 0) {
                return -EINVAL;
+       }
        printk("fb%d: %s frame buffer device\n",
               GET_FB_IDX(ACCESS_FBINFO(fbcon.node)), ACCESS_FBINFO(fbcon.modename));
        if (ACCESS_FBINFO(currcon) < 0) {
@@ -5781,8 +6113,8 @@ __initfunc(int matrox_of_init(struct device_node *dp)) {
 
 #else
 
-MODULE_AUTHOR("(c) 1998 Petr Vandrovec <vandrove@vc.cvut.cz>");
-MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200");
+MODULE_AUTHOR("(c) 1998,1999 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400");
 MODULE_PARM(mem, "i");
 MODULE_PARM_DESC(mem, "Size of available memory in MB, KB or B (2,4,8,12,16MB, default=autodetect)");
 MODULE_PARM(disabled, "i");
@@ -5802,7 +6134,7 @@ MODULE_PARM_DESC(noinit, "Disables W/SG/SD-RAM and bus interface initialization
 MODULE_PARM(mtrr, "i");
 MODULE_PARM_DESC(mtrr, "This speeds up video memory accesses (0=disabled or 1) (default=1)");
 MODULE_PARM(sgram, "i");
-MODULE_PARM_DESC(sgram, "Indicates that G200 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
+MODULE_PARM_DESC(sgram, "Indicates that G200/G400 has SGRAM memory (0=SDRAM, 1=SGRAM) (default=0)");
 MODULE_PARM(inv24, "i");
 MODULE_PARM_DESC(inv24, "Inverts clock polarity for 24bpp and loop frequency > 100MHz (default=do not invert polarity)");
 MODULE_PARM(inverse, "i");
@@ -5855,6 +6187,12 @@ MODULE_PARM(grayscale, "i");
 MODULE_PARM_DESC(grayscale, "Sets display into grayscale. Works perfectly with paletized videomode (4, 8bpp), some limitations apply to 16, 24 and 32bpp videomodes (default=nograyscale)");
 MODULE_PARM(cross4MB, "i");
 MODULE_PARM_DESC(cross4MB, "Specifies that 4MB boundary can be in middle of line. (default=autodetected)");
+#ifdef CONFIG_FB_OF
+MODULE_PARM(vmode, "i");
+MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)");
+MODULE_PARM(cmode, "i");
+MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)");
+#endif
 
 __initfunc(int init_module(void)) {
 
@@ -5862,7 +6200,7 @@ __initfunc(int init_module(void)) {
 
 #ifdef DEBUG
        if( disabled )
-               return 0;
+               return -ENXIO;
 #endif /* DEBUG */
 
        if (depth == 0)
index e8a53f6d4c09c66c04328481e38bb7fefdc4ce2f..8da1765dd480f38cbb1f2d83c1932680b91b44d6 100644 (file)
@@ -296,6 +296,8 @@ static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
        return sp;
 }
 
+static int warnings = 0;
+
 /*
  * These are the functions used to load a.out style executables and shared
  * libraries.  There is no binary dependent code anywhere else.
@@ -311,7 +313,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
        unsigned long fd_offset;
        unsigned long rlim;
        int retval;
-       static int warnings = 0;
 
        ex = *((struct exec *) bprm->buf);              /* exec-header */
        if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
@@ -329,15 +330,6 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
                        printk(KERN_NOTICE "N_TXTOFF != BLOCK_SIZE. See a.out.h.\n");
                return -ENOEXEC;
        }
-
-       if (N_MAGIC(ex) == ZMAGIC && ex.a_text &&
-           bprm->dentry->d_inode->i_op &&
-           bprm->dentry->d_inode->i_op->bmap &&
-           (fd_offset < bprm->dentry->d_inode->i_sb->s_blocksize)) {
-               if(warnings++<10)
-                       printk(KERN_NOTICE "N_TXTOFF < BLOCK_SIZE. Please convert binary.\n");
-               return -ENOEXEC;
-       }
 #endif
 
        /* Check initial limits. This avoids letting people circumvent
@@ -415,11 +407,17 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
                fd = open_dentry(bprm->dentry, O_RDONLY);
                if (fd < 0)
                        return fd;
-               file = fcheck(fd);
-
-               if (!file->f_op || !file->f_op->mmap) {
+               file = fget(fd);
+
+               if (!file->f_op || !file->f_op->mmap ||
+                   fd_offset & (bprm->dentry->d_inode->i_sb->s_blocksize-1)) {
+                       if (warnings++<10)
+                               printk(KERN_NOTICE
+                                      "fd_offset is not blocksize aligned. Loading %s in anonymous memory.\n",
+                                      file->f_dentry->d_name.name);
+                       fput(file);
                        sys_close(fd);
-                       do_mmap(NULL, 0, ex.a_text+ex.a_data,
+                       do_mmap(NULL, N_TXTADDR(ex), ex.a_text+ex.a_data,
                                PROT_READ|PROT_WRITE|PROT_EXEC,
                                MAP_FIXED|MAP_PRIVATE, 0);
                        read_exec(bprm->dentry, fd_offset,
@@ -436,6 +434,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
                        fd_offset);
 
                if (error != N_TXTADDR(ex)) {
+                       fput(file);
                        sys_close(fd);
                        send_sig(SIGKILL, current, 0);
                        return error;
@@ -445,6 +444,7 @@ static inline int do_load_aout_binary(struct linux_binprm * bprm, struct pt_regs
                                PROT_READ | PROT_WRITE | PROT_EXEC,
                                MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
                                fd_offset + ex.a_text);
+               fput(file);
                sys_close(fd);
                if (error != N_DATADDR(ex)) {
                        send_sig(SIGKILL, current, 0);
@@ -499,7 +499,6 @@ do_load_aout_library(int fd)
        unsigned long error;
        int retval;
        loff_t offset = 0;
-       static int warnings = 0;
        struct exec ex;
 
        retval = -EACCES;
@@ -525,13 +524,6 @@ do_load_aout_library(int fd)
                goto out_putf;
        }
 
-       if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
-           (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
-               if(warnings++<10)
-                       printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
-               goto out_putf;
-       }
-
        if (N_FLAGS(ex))
                goto out_putf;
 
@@ -540,6 +532,20 @@ do_load_aout_library(int fd)
 
        start_addr =  ex.a_entry & 0xfffff000;
 
+       if (N_TXTOFF(ex) & (inode->i_sb->s_blocksize-1)) {
+               if (warnings++<10)
+                       printk(KERN_NOTICE
+                              "N_TXTOFF is not blocksize aligned. Loading library %s in anonymous memory.\n",
+                              file->f_dentry->d_name.name);
+               do_mmap(NULL, start_addr, ex.a_text + ex.a_data,
+                       PROT_READ | PROT_WRITE | PROT_EXEC,
+                       MAP_FIXED| MAP_PRIVATE, 0);
+               read_exec(file->f_dentry, N_TXTOFF(ex),
+                         (char *)start_addr, ex.a_text + ex.a_data, 0);
+               flush_icache_range((unsigned long) start_addr,
+                                  (unsigned long) start_addr + ex.a_text + ex.a_data);
+               goto map_bss;
+       }
        /* Now use mmap to map the library into memory. */
        error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
                        PROT_READ | PROT_WRITE | PROT_EXEC,
@@ -549,6 +555,7 @@ do_load_aout_library(int fd)
        if (error != start_addr)
                goto out_putf;
 
+ map_bss:
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
        if (bss > len) {
index 1e8b1ac19a9f8b57504d30b62c7a4b7cd312aa54..2630078f74a9e0ce79d5853a7387e7d589ccfd3c 100644 (file)
@@ -1341,7 +1341,6 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr, uid_t initiator)
                dquot_incr_blocks(transfer_to[cnt], blocks);
 
                unlock_dquot(transfer_from[cnt]);
-               dqput(transfer_from[cnt]);
                if (inode->i_dquot[cnt] != NODQUOT) {
                        struct dquot *temp = inode->i_dquot[cnt];
                        inode->i_dquot[cnt] = transfer_to[cnt];
@@ -1351,20 +1350,20 @@ int dquot_transfer(struct dentry *dentry, struct iattr *iattr, uid_t initiator)
                        unlock_dquot(transfer_to[cnt]);
                        dqput(transfer_to[cnt]);
                }
+               dqput(transfer_from[cnt]);
        }
 
        return 0;
 put_all:
        for (disc = 0; disc < cnt; disc++) {
                /* There should be none or both pointers set but... */
-               if (transfer_to[disc] != NODQUOT) {
+               if (transfer_to[disc] != NODQUOT)
                        unlock_dquot(transfer_to[disc]);
-                       dqput(transfer_to[disc]);
-               }
-               if (transfer_from[disc] != NODQUOT) {
+               if (transfer_from[disc] != NODQUOT)
                        unlock_dquot(transfer_from[disc]);
-                       dqput(transfer_from[disc]);
-               }
+               /* dqput() tests for NODQUOT itself... */
+               dqput(transfer_from[disc]);
+               dqput(transfer_to[disc]);
        }
        return error;
 }
diff --git a/include/asm-i386/sgi-cobalt.h b/include/asm-i386/sgi-cobalt.h
new file mode 100644 (file)
index 0000000..7bbac73
--- /dev/null
@@ -0,0 +1,119 @@
+#include <linux/config.h>
+#ifndef __I386_SGI_COBALT_H
+#define __I386_SGI_COBALT_H
+
+/*
+ * Cobalt SGI Visual Workstation system ASIC
+ */ 
+
+#define        CO_CPU_PHYS             0xc2000000
+#define        CO_APIC_PHYS            0xc4000000
+
+/* see set_fixmap() and asm/fixmap.h */
+#define        CO_CPU_VADDR            (fix_to_virt(FIX_CO_CPU))
+#define        CO_APIC_VADDR           (fix_to_virt(FIX_CO_APIC))
+
+/* Cobalt CPU registers -- relative to CO_CPU_VADDR, use co_cpu_*() */
+#define        CO_CPU_REV              0x08
+#define        CO_CPU_CTRL             0x10
+#define        CO_CPU_STAT             0x20
+#define        CO_CPU_TIMEVAL          0x30
+
+/* CO_CPU_CTRL bits */
+#define        CO_CTRL_TIMERUN         0x04            /* 0 == disabled */
+#define        CO_CTRL_TIMEMASK        0x08            /* 0 == unmasked */
+
+/* CO_CPU_STATUS bits */
+#define        CO_STAT_TIMEINTR        0x02    /* (r) 1 == int pend, (w) 0 == clear */
+
+/* CO_CPU_TIMEVAL value */
+#define        CO_TIME_HZ              100000000       /* Cobalt core rate */
+
+/* Cobalt APIC registers -- relative to CO_APIC_VADDR, use co_apic_*() */
+#define        CO_APIC_HI(n)           (((n) * 0x10) + 4)
+#define        CO_APIC_LO(n)           ((n) * 0x10)
+#define        CO_APIC_ID              0x0ffc
+
+/* CO_APIC_ID bits */
+#define        CO_APIC_ENABLE          0x00000100
+
+/* CO_APIC_LO bits */
+#define        CO_APIC_MASK            0x00010000      /* 0 = enabled */
+#define        CO_APIC_LEVEL           0x00008000      /* 0 = edge */
+
+/*
+ * Where things are physically wired to Cobalt
+ * #defines with no board _<type>_<rev>_ are common to all (thus far)
+ */
+#define        CO_APIC_IDE0            4
+#define CO_APIC_IDE1           2               /* Only on 320 */
+
+#define        CO_APIC_8259            12              /* serial, floppy, par-l-l */
+
+/* Lithium PCI Bridge A -- "the one with 82557 Ethernet" */
+#define        CO_APIC_PCIA_BASE0      0 /* and 1 */   /* slot 0, line 0 */
+#define        CO_APIC_PCIA_BASE123    5 /* and 6 */   /* slot 0, line 1 */
+
+#define        CO_APIC_PIIX4_USB       7               /* this one is wierd */
+
+/* Lithium PCI Bridge B -- "the one with PIIX4" */
+#define        CO_APIC_PCIB_BASE0      8 /* and 9-12 *//* slot 0, line 0 */
+#define        CO_APIC_PCIB_BASE123    13 /* 14.15 */  /* slot 0, line 1 */
+
+#define        CO_APIC_VIDOUT0         16
+#define        CO_APIC_VIDOUT1         17
+#define        CO_APIC_VIDIN0          18
+#define        CO_APIC_VIDIN1          19
+
+#define        CO_APIC_LI_AUDIO        22
+
+#define        CO_APIC_AS              24
+#define        CO_APIC_RE              25
+
+#define CO_APIC_CPU            28              /* Timer and Cache interrupt */
+#define        CO_APIC_NMI             29
+#define        CO_APIC_LAST            CO_APIC_NMI
+
+/*
+ * This is how irqs are assigned on the Visual Workstation.
+ * Legacy devices get irq's 1-15 (system clock is 0 and is CO_APIC_CPU).
+ * All other devices (including PCI) go to Cobalt and are irq's 16 on up.
+ */
+#define        CO_IRQ_APIC0    16                      /* irq of apic entry 0 */
+#define        IS_CO_APIC(irq) ((irq) >= CO_IRQ_APIC0)
+#define        CO_IRQ(apic)    (CO_IRQ_APIC0 + (apic)) /* apic ent to irq */
+#define        CO_APIC(irq)    ((irq) - CO_IRQ_APIC0)  /* irq to apic ent */
+#define CO_IRQ_IDE0    14                      /* knowledge of... */
+#define CO_IRQ_IDE1    15                      /* ... ide driver defaults! */
+#define        CO_IRQ_8259     CO_IRQ(CO_APIC_8259)
+
+#ifdef CONFIG_X86_VISWS_APIC
+extern __inline void co_cpu_write(unsigned long reg, unsigned long v)
+{
+       *((volatile unsigned long *)(CO_CPU_VADDR+reg))=v;
+}
+
+extern __inline unsigned long co_cpu_read(unsigned long reg)
+{
+       return *((volatile unsigned long *)(CO_CPU_VADDR+reg));
+}            
+             
+extern __inline void co_apic_write(unsigned long reg, unsigned long v)
+{
+       *((volatile unsigned long *)(CO_APIC_VADDR+reg))=v;
+}            
+             
+extern __inline unsigned long co_apic_read(unsigned long reg)
+{
+       return *((volatile unsigned long *)(CO_APIC_VADDR+reg));
+}
+#endif
+
+extern char visws_board_type;
+
+#define        VISWS_320       0
+#define        VISWS_540       1
+
+extern char visws_board_rev;
+
+#endif
index f084ca0366b17e79a352d3f4b8a38e864f402a7b..aaf0a44365ff9481bbe1e13815b442fee93137c8 100644 (file)
@@ -72,6 +72,7 @@
 #define FB_ACCEL_SUN_BWTWO     23      /* Sun bwtwo                     */
 #define FB_ACCEL_SUN_CGTHREE   24      /* Sun cgthree                   */
 #define FB_ACCEL_SUN_TCX       25      /* Sun tcx                       */
+#define FB_ACCEL_MATROX_MGAG400 26     /* Matrox G400                   */
 
 struct fb_fix_screeninfo {
        char id[16];                    /* identification string eg "TT Builtin" */
index 6082de3dd76abecdcf608c61a441d6c9bdcc66da..f674884d82139bc8308eb9afdeb76f87108fd73e 100644 (file)
@@ -38,6 +38,7 @@ struct i2c_device;
 
 #define I2C_BUSID_BT848                1       /* I2C bus on a BT848 */
        /* 2 is used in 2.3.x */
+#define I2C_BUSID_SGIVWFB      1       /* From the SGI official patch */
 #define I2C_BUSID_BUZ          3       /* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN                4       /* I2C bus on a Zoran */
 
index 0dc98d55612ba6c65730f2aa25b66c31a0e5f80f..232c53dcb32959afa52cc007aa952135f7d6f891 100644 (file)
@@ -292,7 +292,7 @@ extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t p
 
 extern void vmtruncate(struct inode * inode, unsigned long offset);
 extern int handle_mm_fault(struct task_struct *tsk,struct vm_area_struct *vma, unsigned long address, int write_access);
-extern void make_pages_present(unsigned long addr, unsigned long end);
+extern int make_pages_present(unsigned long addr, unsigned long end);
 
 extern int pgt_cache_water[2];
 extern int check_pgt_cache(void);
@@ -300,7 +300,6 @@ extern int check_pgt_cache(void);
 extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
 extern void mem_init(unsigned long start_mem, unsigned long end_mem);
 extern void show_mem(void);
-extern void oom(struct task_struct * tsk);
 extern void si_meminfo(struct sysinfo * val);
 
 /* mmap.c */
index 3adbc5ab3d5810eb7437b41244932070e1ad48cc..856fc0168e9015f3ca6be34f70c6ddcde3bfe5f5 100644 (file)
@@ -209,6 +209,9 @@ struct parport {
        spinlock_t pardevice_lock;
        spinlock_t waitlist_lock;
        rwlock_t cad_lock;
+
+       /* PCI parallel I/O card support. */
+       unsigned long base_hi;  /* base address (hi - ECR) */
 };
 
 /* parport_register_port registers a new parallel port at the given address (if
index a74c7a12162b9b8bcfa08e4adb116a21322c1e11..f7ef3406de12e7a7a031cfa4578b2ea3d78199b4 100644 (file)
@@ -5,14 +5,15 @@
 
 /* --- register definitions ------------------------------- */
 
-#define ECONTROL 0x402
-#define CONFIGB  0x401
-#define CONFIGA  0x400
-#define EPPDATA  0x4
-#define EPPADDR  0x3
-#define CONTROL  0x2
-#define STATUS   0x1
-#define DATA     0
+#define ECONTROL(p) ((p)->base_hi + 0x2)
+#define CONFIGB(p)  ((p)->base_hi + 0x1)
+#define CONFIGA(p)  ((p)->base_hi + 0x0)
+#define FIFO(p)     ((p)->base_hi + 0x0)
+#define EPPDATA(p)  ((p)->base    + 0x4)
+#define EPPADDR(p)  ((p)->base    + 0x3)
+#define CONTROL(p)  ((p)->base    + 0x2)
+#define STATUS(p)   ((p)->base    + 0x1)
+#define DATA(p)     ((p)->base    + 0x0)
 
 /* Private data for PC low-level driver. */
 struct parport_pc_private {
@@ -26,27 +27,27 @@ extern volatile unsigned char parport_pc_ctr;
 
 extern __inline__ void parport_pc_write_epp(struct parport *p, unsigned char d)
 {
-       outb(d, p->base+EPPDATA);
+       outb(d, EPPDATA(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_epp(struct parport *p)
 {
-       return inb(p->base+EPPDATA);
+       return inb(EPPDATA(p));
 }
 
 extern __inline__ void parport_pc_write_epp_addr(struct parport *p, unsigned char d)
 {
-       outb(d, p->base+EPPADDR);
+       outb(d, EPPADDR(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_epp_addr(struct parport *p)
 {
-       return inb(p->base+EPPADDR);
+       return inb(EPPADDR(p));
 }
 
 extern __inline__ 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;
@@ -54,24 +55,24 @@ extern __inline__ int parport_pc_check_epp_timeout(struct parport *p)
 
 extern __inline__ unsigned char parport_pc_read_configb(struct parport *p)
 {
-       return inb(p->base+CONFIGB);
+       return inb(CONFIGB(p));
 }
 
 extern __inline__ void parport_pc_write_data(struct parport *p, unsigned char d)
 {
-       outb(d, p->base+DATA);
+       outb(d, DATA(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_data(struct parport *p)
 {
-       return inb(p->base+DATA);
+       return inb(DATA(p));
 }
 
 extern __inline__ 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));
 }
 
 extern __inline__ unsigned char parport_pc_read_control(struct parport *p)
@@ -85,34 +86,34 @@ extern __inline__ unsigned char parport_pc_frob_control(struct parport *p, unsig
        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 */
 }
 
 extern __inline__ void parport_pc_write_status(struct parport *p, unsigned char d)
 {
-       outb(d, p->base+STATUS);
+       outb(d, STATUS(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_status(struct parport *p)
 {
-       return inb(p->base+STATUS);
+       return inb(STATUS(p));
 }
 
 extern __inline__ void parport_pc_write_econtrol(struct parport *p, unsigned char d)
 {
-       outb(d, p->base+ECONTROL);
+       outb(d, ECONTROL(p));
 }
 
 extern __inline__ unsigned char parport_pc_read_econtrol(struct parport *p)
 {
-       return inb(p->base+ECONTROL);
+       return inb(ECONTROL(p));
 }
 
 extern __inline__ 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;
 }
 
index 2efb9438c80222f7e0fda5a8f0c94825d1995cd0..bfc2b95c55ae5abadbb0076017de75d9d5e6d36a 100644 (file)
 #define PCI_VENDOR_ID_CBOARDS          0x1307
 #define PCI_DEVICE_ID_CBOARDS_DAS1602_16 0x0001
 
+#define PCI_VENDOR_ID_SIIG             0x131f
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_550        0x1010
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_650        0x1011
+#define PCI_DEVICE_ID_SIIG_1S1P_10x_850        0x1012
+#define PCI_DEVICE_ID_SIIG_1P_10x      0x1020
+#define PCI_DEVICE_ID_SIIG_2P_10x      0x1021
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_550        0x1034
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_650        0x1035
+#define PCI_DEVICE_ID_SIIG_2S1P_10x_850        0x1036
+#define PCI_DEVICE_ID_SIIG_1P_20x      0x2020
+#define PCI_DEVICE_ID_SIIG_2P_20x      0x2021
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_550        0x2040
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_650        0x2041
+#define PCI_DEVICE_ID_SIIG_2P1S_20x_850        0x2042
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_550        0x2010
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_650        0x2011
+#define PCI_DEVICE_ID_SIIG_1S1P_20x_850        0x2012
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_550        0x2060
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_650        0x2061
+#define PCI_DEVICE_ID_SIIG_2S1P_20x_850        0x2062
+
 #define PCI_VENDOR_ID_NETGEAR          0x1385
 #define PCI_DEVICE_ID_NETGEAR_GA620    0x620a
 
+#define PCI_VENDOR_ID_LAVA             0x1407
+#define PCI_DEVICE_ID_LAVA_PARALLEL    0x8000
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A  0x8002 /* The Lava Dual Parallel is */
+#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B  0x8003 /* two PCI devices on a card */
+
 #define PCI_VENDOR_ID_SYMPHONY         0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101     0x0001
 
index 66ba5d6235202fd4da46ead4a7134879d7e2bdda..3403bf4293380410be6a32f69f86de2a1ebe69f6 100644 (file)
@@ -91,8 +91,8 @@ extern void rw_swap_page_nolock(int, unsigned long, char *, int);
 extern void swap_after_unlock_page (unsigned long entry);
 
 /* linux/mm/page_alloc.c */
-extern void swap_in(struct task_struct *, struct vm_area_struct *,
-                   pte_t *, unsigned long, int);
+extern int swap_in(struct task_struct *, struct vm_area_struct *,
+                  pte_t *, unsigned long, int);
 
 
 /* linux/mm/swap_state.c */
index 3d9538e727ed8e104311f06b3a6c6284be68897c..7c21bae24d6f260803c9ea381df3b1a14c373a4b 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -639,10 +639,8 @@ static unsigned long shm_nopage(struct vm_area_struct * shmd, unsigned long addr
        pte = __pte(shp->shm_pages[idx]);
        if (!pte_present(pte)) {
                unsigned long page = get_free_page(GFP_USER);
-               if (!page) {
-                       oom(current);
-                       return 0;
-               }
+               if (!page)
+                       return -1;
                pte = __pte(shp->shm_pages[idx]);
                if (pte_present(pte)) {
                        free_page (page); /* doesn't sleep */
index 68e3f8c2b67d99ae7777c0a266e1f2275dc0fc71..d063ad72d954b970f2e836918c5784e8429445ed 100644 (file)
@@ -987,7 +987,7 @@ found_page:
        if (no_share && !new_page) {
                new_page = page_cache_alloc();
                if (!new_page)
-                       goto failure;
+                       goto release_and_oom;
        }
 
        if (PageLocked(page))
@@ -1035,7 +1035,7 @@ no_cached_page:
        if (!new_page)
                new_page = page_cache_alloc();
        if (!new_page)
-               goto no_page;
+               goto oom;
 
        /*
         * During getting the above page we might have slept,
@@ -1089,6 +1089,11 @@ failure:
                page_cache_free(new_page);
 no_page:
        return 0;
+
+release_and_oom:
+       page_cache_release(page);
+oom:
+       return -1;
 }
 
 /*
index 6a7a8a9e3acb01fa28047ebb9d9daa60c78d04ab..47cd98dc05786daab75fdf6f1575263d0a23f915 100644 (file)
@@ -61,16 +61,6 @@ static inline void copy_cow_page(unsigned long from, unsigned long to)
 
 mem_map_t * mem_map = NULL;
 
-/*
- * oom() prints a message (so that the user knows why the process died),
- * and gives the process an untrappable SIGKILL.
- */
-void oom(struct task_struct * task)
-{
-       printk("\nOut of memory for %s.\n", task->comm);
-       force_sig(SIGKILL, task);
-}
-
 /*
  * Note: this doesn't free the actual pages themselves. That
  * has been handled earlier when unmapping all the memory regions.
@@ -577,13 +567,13 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
        pmd = pmd_alloc(pgd, address);
        if (!pmd) {
                free_page(page);
-               oom(tsk);
+               force_sig(SIGKILL, tsk);
                return 0;
        }
        pte = pte_alloc(pmd, address);
        if (!pte) {
                free_page(page);
-               oom(tsk);
+               force_sig(SIGKILL, tsk);
                return 0;
        }
        if (!pte_none(*pte)) {
@@ -687,12 +677,11 @@ end_wp_page:
 
 bad_wp_page:
        printk("do_wp_page: bogus page at address %08lx (%08lx)\n",address,old_page);
-       send_sig(SIGKILL, tsk, 1);
 no_new_page:
        unlock_kernel();
        if (new_page)
                free_page(new_page);
-       return 0;
+       return -1;
 }
 
 /*
@@ -789,8 +778,9 @@ static int do_swap_page(struct task_struct * tsk,
        struct vm_area_struct * vma, unsigned long address,
        pte_t * page_table, pte_t entry, int write_access)
 {
+       int ret = 1;
        if (!vma->vm_ops || !vma->vm_ops->swapin) {
-               swap_in(tsk, vma, page_table, pte_val(entry), write_access);
+               ret = swap_in(tsk, vma, page_table, pte_val(entry), write_access);
                flush_page_to_ram(pte_page(*page_table));
        } else {
                pte_t page = vma->vm_ops->swapin(vma, address - vma->vm_start + vma->vm_offset, pte_val(entry));
@@ -807,7 +797,7 @@ static int do_swap_page(struct task_struct * tsk,
                }
        }
        unlock_kernel();
-       return 1;
+       return ret;
 }
 
 /*
@@ -819,7 +809,7 @@ static int do_anonymous_page(struct task_struct * tsk, struct vm_area_struct * v
        if (write_access) {
                unsigned long page = __get_free_page(GFP_USER);
                if (!page)
-                       return 0;
+                       return -1;
                clear_page(page);
                entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
                vma->vm_mm->rss++;
@@ -865,6 +855,8 @@ static int do_no_page(struct task_struct * tsk, struct vm_area_struct * vma,
        unlock_kernel();
        if (!page)
                return 0;
+       if (page == -1)
+               return -1;
 
        ++tsk->maj_flt;
        ++vma->vm_mm->rss;
@@ -937,25 +929,26 @@ int handle_mm_fault(struct task_struct *tsk, struct vm_area_struct * vma,
 {
        pgd_t *pgd;
        pmd_t *pmd;
+       pte_t * pte;
+       int ret;
 
        pgd = pgd_offset(vma->vm_mm, address);
        pmd = pmd_alloc(pgd, address);
-       if (pmd) {
-               pte_t * pte = pte_alloc(pmd, address);
-               if (pte) {
-                       if (handle_pte_fault(tsk, vma, address, write_access, pte)) {
-                               update_mmu_cache(vma, address, *pte);
-                               return 1;
-                       }
-               }
-       }
-       return 0;
+       if (!pmd)
+               return -1;
+       pte = pte_alloc(pmd, address);
+       if (!pte)
+               return -1;
+       ret = handle_pte_fault(tsk, vma, address, write_access, pte);
+       if (ret > 0)
+               update_mmu_cache(vma, address, *pte);
+       return ret;
 }
 
 /*
  * Simplistic page force-in..
  */
-void make_pages_present(unsigned long addr, unsigned long end)
+int make_pages_present(unsigned long addr, unsigned long end)
 {
        int write;
        struct vm_area_struct * vma;
@@ -963,7 +956,9 @@ void make_pages_present(unsigned long addr, unsigned long end)
        vma = find_vma(current->mm, addr);
        write = (vma->vm_flags & VM_WRITE) != 0;
        while (addr < end) {
-               handle_mm_fault(current, vma, addr, write);
+               if (handle_mm_fault(current, vma, addr, write) < 0)
+                       return -1;
                addr += PAGE_SIZE;
        }
+       return 0;
 }
index f727f91e04d5f3f46af8c239712c15741d28dbdc..72719484bcc09d8699e9cec0e128209f24b55af7 100644 (file)
@@ -388,7 +388,7 @@ void swapin_readahead(unsigned long entry)
  * Also, don't bother to add to the swap cache if this page-in
  * was due to a write access.
  */
-void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
+int swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
        pte_t * page_table, unsigned long entry, int write_access)
 {
        unsigned long page;
@@ -401,14 +401,10 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
        if (pte_val(*page_table) != entry) {
                if (page_map)
                        free_page_and_swap_cache(page_address(page_map));
-               return;
-       }
-       if (!page_map) {
-               set_pte(page_table, BAD_PAGE);
-               swap_free(entry);
-               oom(tsk);
-               return;
+               return 1;
        }
+       if (!page_map)
+               return -1;
 
        page = page_address(page_map);
        vma->vm_mm->rss++;
@@ -417,7 +413,7 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
 
        if (!write_access || is_page_shared(page_map)) {
                set_pte(page_table, mk_pte(page, vma->vm_page_prot));
-               return;
+               return 1;
        }
 
        /*
@@ -427,5 +423,5 @@ void swap_in(struct task_struct * tsk, struct vm_area_struct * vma,
         */
        delete_from_swap_cache(page_map);
        set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-       return;
+       return 1;
 }
index 94d89450c97e29d1c8c33f011cb0c5d05cae3720..02b34f9cc68c94d1e3c5335d680c4e6e902b120a 100644 (file)
@@ -130,15 +130,17 @@ void swap_free(unsigned long entry)
        offset = SWP_OFFSET(entry);
        if (offset >= p->max)
                goto bad_offset;
-       if (offset < p->lowest_bit)
-               p->lowest_bit = offset;
-       if (offset > p->highest_bit)
-               p->highest_bit = offset;
        if (!p->swap_map[offset])
                goto bad_free;
        if (p->swap_map[offset] < SWAP_MAP_MAX) {
                if (!--p->swap_map[offset])
+               {
+                       if (offset < p->lowest_bit)
+                               p->lowest_bit = offset;
+                       if (offset > p->highest_bit)
+                               p->highest_bit = offset;
                        nr_swap_pages++;
+               }
        }
 #ifdef DEBUG_SWAP
        printk("DebugVM: swap_free(entry %08lx, count now %d)\n",
index d651e6f949fd350401d84d29a9356a73a998dd76..f150a052cac73a2a6352351251241557838c9b08 100644 (file)
@@ -308,7 +308,8 @@ static int swap_out_process(struct task_struct * p, int gfp_mask)
 static int swap_out(unsigned int priority, int gfp_mask)
 {
        struct task_struct * p, * pbest;
-       int counter, assign, max_cnt;
+       int assign = 0, counter;
+       unsigned long max_cnt;
 
        /* 
         * We make one or two passes through the task list, indexed by 
@@ -327,11 +328,8 @@ static int swap_out(unsigned int priority, int gfp_mask)
        counter = nr_tasks / (priority+1);
        if (counter < 1)
                counter = 1;
-       if (counter > nr_tasks)
-               counter = nr_tasks;
 
        for (; counter >= 0; counter--) {
-               assign = 0;
                max_cnt = 0;
                pbest = NULL;
        select:
@@ -343,7 +341,7 @@ static int swap_out(unsigned int priority, int gfp_mask)
                        if (p->mm->rss <= 0)
                                continue;
                        /* Refresh swap_cnt? */
-                       if (assign)
+                       if (assign == 1)
                                p->mm->swap_cnt = p->mm->rss;
                        if (p->mm->swap_cnt > max_cnt) {
                                max_cnt = p->mm->swap_cnt;
@@ -351,6 +349,8 @@ static int swap_out(unsigned int priority, int gfp_mask)
                        }
                }
                read_unlock(&tasklist_lock);
+               if (assign == 1)
+                       assign = 2;
                if (!pbest) {
                        if (!assign) {
                                assign = 1;
@@ -435,7 +435,7 @@ void __init kswapd_setup(void)
        printk ("Starting kswapd v%.*s\n", i, s);
 }
 
-static struct task_struct *kswapd_process;
+static struct wait_queue * kswapd_wait = NULL;
 
 /*
  * The background pageout daemon, started as a kernel thread
@@ -455,7 +455,6 @@ int kswapd(void *unused)
 {
        struct task_struct *tsk = current;
 
-       kswapd_process = tsk;
        tsk->session = 1;
        tsk->pgrp = 1;
        strcpy(tsk->comm, "kswapd");
@@ -484,16 +483,18 @@ int kswapd(void *unused)
                 * the processes needing more memory will wake us
                 * up on a more timely basis.
                 */
-               do {
-                       if (nr_free_pages >= freepages.high)
-                               break;
-
-                       if (!do_try_to_free_pages(GFP_KSWAPD))
-                               break;
-               } while (!tsk->need_resched);
-               run_task_queue(&tq_disk);
-               tsk->state = TASK_INTERRUPTIBLE;
-               schedule_timeout(HZ);
+               interruptible_sleep_on_timeout(&kswapd_wait, HZ);
+               while (nr_free_pages < freepages.high)
+               {
+                       if (do_try_to_free_pages(GFP_KSWAPD))
+                       {
+                               if (tsk->need_resched)
+                                       schedule();
+                               continue;
+                       }
+                       tsk->state = TASK_INTERRUPTIBLE;
+                       schedule_timeout(10*HZ);
+               }
        }
 }
 
@@ -516,7 +517,7 @@ int try_to_free_pages(unsigned int gfp_mask)
 {
        int retval = 1;
 
-       wake_up_process(kswapd_process);
+       wake_up_interruptible(&kswapd_wait);
        if (gfp_mask & __GFP_WAIT)
                retval = do_try_to_free_pages(gfp_mask);
        return retval;
index 3aa790da4b2e5873139f3d1a46b42f8e31639d6b..076f07b066389ca68d9381c2690cf8aedafc81fb 100644 (file)
@@ -10,6 +10,7 @@
  * Authors:    see ip.c
  *
  * Fixes:
+ *             Joseph Gooch    :       Removed maddr selection for ip_masq, now done in ip_masq.c
  *             Many            :       Split from ip.c , see ip_input.c for 
  *                                     history.
  *             Dave Gregorich  :       NULL ip_rt_put fix for multicast 
@@ -168,7 +169,6 @@ int ip_forward(struct sk_buff *skb)
                 *      and skip the firewall checks
                 */
                if (iph->protocol == IPPROTO_ICMP) {
-                       __u32 maddr;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
                        struct icmphdr *icmph = (struct icmphdr *)((char*)iph + (iph->ihl << 2));
                        if ((icmph->type==ICMP_DEST_UNREACH)||
@@ -187,7 +187,7 @@ int ip_forward(struct sk_buff *skb)
                                        /* ICMP matched - skip firewall */
                                        goto skip_call_fw_firewall;
 #ifdef CONFIG_IP_MASQUERADE_ICMP
-                              }
+                       }
 #endif                         
                }
                if (rt->rt_flags&RTCF_MASQ)
index 6d0588c005bdeb6f02362bb14ef721d35669d969..e9e88a675c98a998c968dfaef8dd2d434defc6e2 100644 (file)
@@ -10,6 +10,9 @@
  *     See ip_fw.c for original log
  *
  * Fixes:
+ *     Joseph Gooch            :       Modified ip_fw_masquerade() to do a ip_route_output()
+ *      (help by Dan Drown)    :       to choose the proper local address.
+ *      (and Alexey)           :
  *     Juan Jose Ciarlante     :       Modularized application masquerading (see ip_masq_app.c)
  *     Juan Jose Ciarlante     :       New struct ip_masq_seq that holds output/input delta seq.
  *     Juan Jose Ciarlante     :       Added hashed lookup by proto,maddr,mport and proto,saddr,sport
@@ -1141,6 +1144,22 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr)
                return -1;
        }
 
+       /* Lets determine our maddr now, shall we? */
+       if (maddr == 0) {
+               struct rtable *rt;
+               struct rtable *skb_rt = (struct rtable*)skb->dst;
+               struct device *skb_dev = skb_rt->u.dst.dev;
+
+               if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos)|RTO_CONN, skb_dev?skb_dev->ifindex:0)) {
+                       /* Fallback on old method */
+                       maddr = inet_select_addr(skb_dev, skb_rt->rt_gateway, RT_SCOPE_UNIVERSE);
+               } else {
+                       /* Route lookup succeeded */
+                       maddr = rt->rt_src;
+                       ip_rt_put(rt);
+               }
+       }
+
        switch (iph->protocol) {
        case IPPROTO_ICMP:
                return(ip_fw_masq_icmp(skb_p, maddr));
index f369f03ddee6c7679efe7cf22186822b1cb32fd5..f95987d182f02842ec42b04a183ccc94ce196b8f 100644 (file)
@@ -100,7 +100,7 @@ static int ip_masq_user_maddr(struct ip_masq_user *ums)
                return ret;
        }
        dev = rt->u.dst.dev;
-       ums->maddr = ip_masq_select_addr(dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+       ums->maddr = rt->rt_src;  /* Per Alexey */
 
        IP_MASQ_DEBUG(1-debug, "did setup maddr=%lX\n", ntohl(ums->maddr));
        ip_rt_put(rt);
index ab7354e1d2dace4dbbc40fda2ecd867179302a21..c36a78a3bd8d8daa94de5fc28f7e530c2ea79f24 100644 (file)
@@ -119,7 +119,9 @@ EXPORT_SYMBOL(irda_debug);
 EXPORT_SYMBOL(irda_notify_init);
 EXPORT_SYMBOL(irmanager_notify);
 EXPORT_SYMBOL(irda_lock);
+#ifdef CONFIG_PROC_FS
 EXPORT_SYMBOL(proc_irda);
+#endif
 
 /* IrIAP/IrIAS */
 EXPORT_SYMBOL(iriap_getvaluebyclass_request);