Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
Linus Torvalds [Fri, 3 Feb 2006 16:33:06 +0000 (08:33 -0800)]
147 files changed:
Documentation/cputopology.txt [new file with mode: 0644]
Documentation/driver-model/overview.txt
Documentation/parport-lowlevel.txt
Documentation/pci-error-recovery.txt
MAINTAINERS
Makefile
arch/i386/oprofile/backtrace.c
arch/ia64/kernel/topology.c
arch/s390/kernel/compat_wrapper.S
arch/v850/kernel/simcons.c
arch/xtensa/platform-iss/console.c
drivers/base/Makefile
drivers/base/topology.c [new file with mode: 0644]
drivers/block/umem.c
drivers/char/cyclades.c
drivers/char/esp.c
drivers/char/ip2/i2cmd.c
drivers/char/ip2main.c
drivers/char/ipmi/ipmi_msghandler.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/char/rio/cirrus.h
drivers/char/rio/defaults.h
drivers/char/rio/link.h
drivers/char/rio/list.h
drivers/char/rio/parmmap.h
drivers/char/rio/phb.h
drivers/char/rio/pkt.h
drivers/char/rio/qbuf.h
drivers/char/rio/riotypes.h
drivers/char/rio/rup.h
drivers/char/rio/sam.h
drivers/char/rocket.c
drivers/char/sx.c
drivers/char/tty_io.c
drivers/char/watchdog/sbc_epx_c3.c
drivers/edac/Kconfig
drivers/edac/e752x_edac.c
drivers/edac/edac_mc.c
drivers/ide/Kconfig
drivers/ide/ide-disk.c
drivers/ide/ide-io.c
drivers/ide/ide-iops.c
drivers/ide/ide-probe.c
drivers/ide/ide.c
drivers/ide/pci/aec62xx.c
drivers/ide/pci/hpt366.c
drivers/ide/pci/it821x.c
drivers/ide/pci/pdc202xx_new.c
drivers/ide/pci/pdc202xx_old.c
drivers/ide/pci/piix.c
drivers/isdn/hisax/hisax.h
drivers/isdn/sc/ioctl.c
drivers/md/md.c
drivers/md/raid0.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/md/raid6main.c
drivers/message/i2o/core.h
drivers/message/i2o/pci.c
drivers/mtd/maps/dc21285.c
drivers/net/3c59x.c
drivers/parport/Kconfig
drivers/parport/Makefile
drivers/parport/ieee1284.c
drivers/parport/parport_ip32.c [new file with mode: 0644]
drivers/parport/parport_serial.c
drivers/parport/probe.c
drivers/s390/block/Kconfig
drivers/s390/block/Makefile
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_eckd.h
drivers/s390/block/dasd_eer.c [new file with mode: 0644]
drivers/s390/block/dasd_int.h
drivers/s390/cio/chsc.h
drivers/serial/Kconfig
drivers/serial/jsm/jsm.h
drivers/serial/jsm/jsm_driver.c
drivers/serial/jsm/jsm_tty.c
drivers/serial/mcfserial.c
drivers/telephony/ixj.c
fs/9p/conv.c
fs/9p/mux.c
fs/9p/vfs_inode.c
fs/buffer.c
fs/dcache.c
fs/direct-io.c
fs/ext2/acl.c
fs/ext2/ialloc.c
fs/ext2/super.c
fs/ext3/acl.c
fs/fat/file.c
fs/fat/misc.c
fs/fcntl.c
fs/jffs/intrep.c
fs/libfs.c
fs/lockd/clntproc.c
fs/nfs/direct.c
fs/proc/proc_misc.c
fs/quota_v2.c
fs/reiserfs/super.c
fs/udf/balloc.c
fs/udf/namei.c
fs/ufs/inode.c
fs/ufs/super.c
fs/ufs/truncate.c
include/asm-cris/bitops.h
include/asm-frv/bitops.h
include/asm-h8300/bitops.h
include/asm-i386/topology.h
include/asm-ia64/ide.h
include/asm-ia64/topology.h
include/asm-s390/dasd.h
include/asm-s390/io.h
include/asm-s390/timer.h
include/asm-v850/bitops.h
include/asm-x86_64/kexec.h
include/asm-x86_64/topology.h
include/linux/bitops.h
include/linux/dcache.h
include/linux/elfcore.h
include/linux/i2o.h
include/linux/ide.h
include/linux/kbd_kern.h
include/linux/list.h
include/linux/lockd/lockd.h
include/linux/parport.h
include/linux/quotaops.h
include/linux/rcupdate.h
include/linux/sunrpc/auth.h
include/linux/suspend.h
include/linux/tty.h
include/linux/tty_flip.h
include/linux/ufs_fs.h
include/linux/ufs_fs_sb.h
init/Kconfig
kernel/cpuset.c
kernel/kprobes.c
kernel/module.c
kernel/signal.c
kernel/time.c
lib/int_sqrt.c
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_unix.c
net/sunrpc/rpc_pipe.c
security/keys/keyctl.c

diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt
new file mode 100644 (file)
index 0000000..ff280e2
--- /dev/null
@@ -0,0 +1,41 @@
+
+Export cpu topology info by sysfs. Items (attributes) are similar
+to /proc/cpuinfo.
+
+1) /sys/devices/system/cpu/cpuX/topology/physical_package_id:
+represent the physical package id of  cpu X;
+2) /sys/devices/system/cpu/cpuX/topology/core_id:
+represent the cpu core id to cpu X;
+3) /sys/devices/system/cpu/cpuX/topology/thread_siblings:
+represent the thread siblings to cpu X in the same core;
+4) /sys/devices/system/cpu/cpuX/topology/core_siblings:
+represent the thread siblings to cpu X in the same physical package;
+
+To implement it in an architecture-neutral way, a new source file,
+driver/base/topology.c, is to export the 5 attributes.
+
+If one architecture wants to support this feature, it just needs to
+implement 4 defines, typically in file include/asm-XXX/topology.h.
+The 4 defines are:
+#define topology_physical_package_id(cpu)
+#define topology_core_id(cpu)
+#define topology_thread_siblings(cpu)
+#define topology_core_siblings(cpu)
+
+The type of **_id is int.
+The type of siblings is cpumask_t.
+
+To be consistent on all architectures, the 4 attributes should have
+deafult values if their values are unavailable. Below is the rule.
+1) physical_package_id: If cpu has no physical package id, -1 is the
+default value.
+2) core_id: If cpu doesn't support multi-core, its core id is 0.
+3) thread_siblings: Just include itself, if the cpu doesn't support
+HT/multi-thread.
+4) core_siblings: Just include itself, if the cpu doesn't support
+multi-core and HT/Multi-thread.
+
+So be careful when declaring the 4 defines in include/asm-XXX/topology.h.
+
+If an attribute isn't defined on an architecture, it won't be exported.
+
index 4466273..ac4a7a7 100644 (file)
@@ -1,50 +1,43 @@
 The Linux Kernel Device Model
 
-Patrick Mochel <mochel@osdl.org>
+Patrick Mochel <mochel@digitalimplant.org>
 
-26 August 2002
+Drafted 26 August 2002
+Updated 31 January 2006
 
 
 Overview
 ~~~~~~~~
 
-This driver model is a unification of all the current, disparate driver models
-that are currently in the kernel. It is intended to augment the
+The Linux Kernel Driver Model is a unification of all the disparate driver
+models that were previously used in the kernel. It is intended to augment the
 bus-specific drivers for bridges and devices by consolidating a set of data
 and operations into globally accessible data structures.
 
-Current driver models implement some sort of tree-like structure (sometimes
-just a list) for the devices they control. But, there is no linkage between
-the different bus types.
+Traditional driver models implemented some sort of tree-like structure
+(sometimes just a list) for the devices they control. There wasn't any
+uniformity across the different bus types.
 
-A common data structure can provide this linkage with little overhead: when a
-bus driver discovers a particular device, it can insert it into the global
-tree as well as its local tree. In fact, the local tree becomes just a subset
-of the global tree.
-
-Common data fields can also be moved out of the local bus models into the
-global model. Some of the manipulations of these fields can also be
-consolidated. Most likely, manipulation functions will become a set
-of helper functions, which the bus drivers wrap around to include any
-bus-specific items.
-
-The common device and bridge interface currently reflects the goals of the
-modern PC: namely the ability to do seamless Plug and Play, power management,
-and hot plug. (The model dictated by Intel and Microsoft (read: ACPI) ensures
-us that any device in the system may fit any of these criteria.)
-
-In reality, not every bus will be able to support such operations. But, most
-buses will support a majority of those operations, and all future buses will.
-In other words, a bus that doesn't support an operation is the exception,
-instead of the other way around.
+The current driver model provides a comon, uniform data model for describing
+a bus and the devices that can appear under the bus. The unified bus
+model includes a set of common attributes which all busses carry, and a set
+of common callbacks, such as device discovery during bus probing, bus
+shutdown, bus power management, etc.
 
+The common device and bridge interface reflects the goals of the modern
+computer: namely the ability to do seamless device "plug and play", power
+management, and hot plug. In particular, the model dictated by Intel and
+Microsoft (namely ACPI) ensures that almost every device on almost any bus
+on an x86-compatible system can work within this paradigm.  Of course,
+not every bus is able to support all such operations, although most
+buses support a most of those operations.
 
 
 Downstream Access
 ~~~~~~~~~~~~~~~~~
 
 Common data fields have been moved out of individual bus layers into a common
-data structure. But, these fields must still be accessed by the bus layers,
+data structure. These fields must still be accessed by the bus layers,
 and sometimes by the device-specific drivers.
 
 Other bus layers are encouraged to do what has been done for the PCI layer.
@@ -53,7 +46,7 @@ struct pci_dev now looks like this:
 struct pci_dev {
        ...
 
-       struct device device;
+       struct device dev;
 };
 
 Note first that it is statically allocated. This means only one allocation on
@@ -64,9 +57,9 @@ the two.
 
 The PCI bus layer freely accesses the fields of struct device. It knows about
 the structure of struct pci_dev, and it should know the structure of struct
-device. PCI devices that have been converted generally do not touch the fields
-of struct device. More precisely, device-specific drivers should not touch
-fields of struct device unless there is a strong compelling reason to do so.
+device. Individual PCI device drivers that have been converted the the current
+driver model generally do not and should not touch the fields of struct device,
+unless there is a strong compelling reason to do so.
 
 This abstraction is prevention of unnecessary pain during transitional phases.
 If the name of the field changes or is removed, then every downstream driver
index 1d40008..8f23024 100644 (file)
@@ -1068,7 +1068,7 @@ SYNOPSIS
 
 struct parport_operations {
        ...
-       void (*write_status) (struct parport *port, unsigned char s);
+       void (*write_control) (struct parport *port, unsigned char s);
        ...
 };
 
@@ -1097,9 +1097,9 @@ SYNOPSIS
 
 struct parport_operations {
        ...
-       void (*frob_control) (struct parport *port,
-                             unsigned char mask,
-                             unsigned char val);
+       unsigned char (*frob_control) (struct parport *port,
+                                      unsigned char mask,
+                                      unsigned char val);
        ...
 };
 
index d089967..634d3e5 100644 (file)
 
                        PCI Error Recovery
                        ------------------
-                         May 31, 2005
-
-               Current document maintainer:
-           Linas Vepstas <linas@austin.ibm.com>
-
-
-Some PCI bus controllers are able to detect certain "hard" PCI errors
-on the bus, such as parity errors on the data and address busses, as
-well as SERR and PERR errors.  These chipsets are then able to disable
-I/O to/from the affected device, so that, for example, a bad DMA
-address doesn't end up corrupting system memory.  These same chipsets
-are also able to reset the affected PCI device, and return it to
-working condition.  This document describes a generic API form
-performing error recovery.
-
-The core idea is that after a PCI error has been detected, there must
-be a way for the kernel to coordinate with all affected device drivers
-so that the pci card can be made operational again, possibly after
-performing a full electrical #RST of the PCI card.  The API below
-provides a generic API for device drivers to be notified of PCI
-errors, and to be notified of, and respond to, a reset sequence.
-
-Preliminary sketch of API, cut-n-pasted-n-modified email from
-Ben Herrenschmidt, circa 5 april 2005
+                        February 2, 2006
+
+                 Current document maintainer:
+             Linas Vepstas <linas@austin.ibm.com>
+
+
+Many PCI bus controllers are able to detect a variety of hardware
+PCI errors on the bus, such as parity errors on the data and address
+busses, as well as SERR and PERR errors.  Some of the more advanced
+chipsets are able to deal with these errors; these include PCI-E chipsets,
+and the PCI-host bridges found on IBM Power4 and Power5-based pSeries
+boxes. A typical action taken is to disconnect the affected device,
+halting all I/O to it.  The goal of a disconnection is to avoid system
+corruption; for example, to halt system memory corruption due to DMA's
+to "wild" addresses. Typically, a reconnection mechanism is also
+offered, so that the affected PCI device(s) are reset and put back
+into working condition. The reset phase requires coordination
+between the affected device drivers and the PCI controller chip.
+This document describes a generic API for notifying device drivers
+of a bus disconnection, and then performing error recovery.
+This API is currently implemented in the 2.6.16 and later kernels.
+
+Reporting and recovery is performed in several steps. First, when
+a PCI hardware error has resulted in a bus disconnect, that event
+is reported as soon as possible to all affected device drivers,
+including multiple instances of a device driver on multi-function
+cards. This allows device drivers to avoid deadlocking in spinloops,
+waiting for some i/o-space register to change, when it never will.
+It also gives the drivers a chance to defer incoming I/O as
+needed.
+
+Next, recovery is performed in several stages. Most of the complexity
+is forced by the need to handle multi-function devices, that is,
+devices that have multiple device drivers associated with them.
+In the first stage, each driver is allowed to indicate what type
+of reset it desires, the choices being a simple re-enabling of I/O
+or requesting a hard reset (a full electrical #RST of the PCI card).
+If any driver requests a full reset, that is what will be done.
+
+After a full reset and/or a re-enabling of I/O, all drivers are
+again notified, so that they may then perform any device setup/config
+that may be required.  After these have all completed, a final
+"resume normal operations" event is sent out.
+
+The biggest reason for choosing a kernel-based implementation rather
+than a user-space implementation was the need to deal with bus
+disconnects of PCI devices attached to storage media, and, in particular,
+disconnects from devices holding the root file system.  If the root
+file system is disconnected, a user-space mechanism would have to go
+through a large number of contortions to complete recovery. Almost all
+of the current Linux file systems are not tolerant of disconnection
+from/reconnection to their underlying block device. By contrast,
+bus errors are easy to manage in the device driver. Indeed, most
+device drivers already handle very similar recovery procedures;
+for example, the SCSI-generic layer already provides significant
+mechanisms for dealing with SCSI bus errors and SCSI bus resets.
+
+
+Detailed Design
+---------------
+Design and implementation details below, based on a chain of
+public email discussions with Ben Herrenschmidt, circa 5 April 2005.
 
 The error recovery API support is exposed to the driver in the form of
 a structure of function pointers pointed to by a new field in struct
-pci_driver. The absence of this pointer in pci_driver denotes an
-"non-aware" driver, behaviour on these is platform dependant.
-Platforms like ppc64 can try to simulate pci hotplug remove/add.
-
-The definition of "pci_error_token" is not covered here. It is based on
-Seto's work on the synchronous error detection. We still need to define
-functions for extracting infos out of an opaque error token. This is
-separate from this API.
+pci_driver. A driver that fails to provide the structure is "non-aware",
+and the actual recovery steps taken are platform dependent.  The
+arch/powerpc implementation will simulate a PCI hotplug remove/add.
 
 This structure has the form:
-
 struct pci_error_handlers
 {
-       int (*error_detected)(struct pci_dev *dev, pci_error_token error);
+       int (*error_detected)(struct pci_dev *dev, enum pci_channel_state);
        int (*mmio_enabled)(struct pci_dev *dev);
-       int (*resume)(struct pci_dev *dev);
        int (*link_reset)(struct pci_dev *dev);
        int (*slot_reset)(struct pci_dev *dev);
+       void (*resume)(struct pci_dev *dev);
 };
 
-A driver doesn't have to implement all of these callbacks. The
-only mandatory one is error_detected(). If a callback is not
-implemented, the corresponding feature is considered unsupported.
-For example, if mmio_enabled() and resume() aren't there, then the
-driver is assumed as not doing any direct recovery and requires
+The possible channel states are:
+enum pci_channel_state {
+       pci_channel_io_normal,  /* I/O channel is in normal state */
+       pci_channel_io_frozen,  /* I/O to channel is blocked */
+       pci_channel_io_perm_failure, /* PCI card is dead */
+};
+
+Possible return values are:
+enum pci_ers_result {
+       PCI_ERS_RESULT_NONE,        /* no result/none/not supported in device driver */
+       PCI_ERS_RESULT_CAN_RECOVER, /* Device driver can recover without slot reset */
+       PCI_ERS_RESULT_NEED_RESET,  /* Device driver wants slot to be reset. */
+       PCI_ERS_RESULT_DISCONNECT,  /* Device has completely failed, is unrecoverable */
+       PCI_ERS_RESULT_RECOVERED,   /* Device driver is fully recovered and operational */
+};
+
+A driver does not have to implement all of these callbacks; however,
+if it implements any, it must implement error_detected(). If a callback
+is not implemented, the corresponding feature is considered unsupported.
+For example, if mmio_enabled() and resume() aren't there, then it
+is assumed that the driver is not doing any direct recovery and requires
 a reset. If link_reset() is not implemented, the card is assumed as
-not caring about link resets, in which case, if recover is supported,
-the core can try recover (but not slot_reset() unless it really did
-reset the slot). If slot_reset() is not supported, link_reset() can
-be called instead on a slot reset.
-
-At first, the call will always be :
-
-       1) error_detected()
-
-       Error detected. This is sent once after an error has been detected. At
-this point, the device might not be accessible anymore depending on the
-platform (the slot will be isolated on ppc64). The driver may already
-have "noticed" the error because of a failing IO, but this is the proper
-"synchronisation point", that is, it gives a chance to the driver to
-cleanup, waiting for pending stuff (timers, whatever, etc...) to
-complete; it can take semaphores, schedule, etc... everything but touch
-the device. Within this function and after it returns, the driver
+not care about link resets. Typically a driver will want to know about
+a slot_reset().
+
+The actual steps taken by a platform to recover from a PCI error
+event will be platform-dependent, but will follow the general
+sequence described below.
+
+STEP 0: Error Event
+-------------------
+PCI bus error is detect by the PCI hardware.  On powerpc, the slot
+is isolated, in that all I/O is blocked: all reads return 0xffffffff,
+all writes are ignored.
+
+
+STEP 1: Notification
+--------------------
+Platform calls the error_detected() callback on every instance of
+every driver affected by the error.
+
+At this point, the device might not be accessible anymore, depending on
+the platform (the slot will be isolated on powerpc). The driver may
+already have "noticed" the error because of a failing I/O, but this
+is the proper "synchronization point", that is, it gives the driver
+a chance to cleanup, waiting for pending stuff (timers, whatever, etc...)
+to complete; it can take semaphores, schedule, etc... everything but
+touch the device. Within this function and after it returns, the driver
 shouldn't do any new IOs. Called in task context. This is sort of a
 "quiesce" point. See note about interrupts at the end of this doc.
 
-       Result codes:
-               - PCIERR_RESULT_CAN_RECOVER:
-                 Driever returns this if it thinks it might be able to recover
+All drivers participating in this system must implement this call.
+The driver must return one of the following result codes:
+               - PCI_ERS_RESULT_CAN_RECOVER:
+                 Driver returns this if it thinks it might be able to recover
                  the HW by just banging IOs or if it wants to be given
-                 a chance to extract some diagnostic informations (see
-                 below).
-               - PCIERR_RESULT_NEED_RESET:
-                 Driver returns this if it thinks it can't recover unless the
-                 slot is reset.
-               - PCIERR_RESULT_DISCONNECT:
-                 Return this if driver thinks it won't recover at all,
-                 (this will detach the driver ? or just leave it
-                 dangling ? to be decided)
-
-So at this point, we have called error_detected() for all drivers
-on the segment that had the error. On ppc64, the slot is isolated. What
-happens now typically depends on the result from the drivers. If all
-drivers on the segment/slot return PCIERR_RESULT_CAN_RECOVER, we would
-re-enable IOs on the slot (or do nothing special if the platform doesn't
-isolate slots) and call 2). If not and we can reset slots, we go to 4),
-if neither, we have a dead slot. If it's an hotplug slot, we might
-"simulate" reset by triggering HW unplug/replug though.
-
->>> Current ppc64 implementation assumes that a device driver will
->>> *not* schedule or semaphore in this routine; the current ppc64
+                 a chance to extract some diagnostic information (see
+                 mmio_enable, below).
+               - PCI_ERS_RESULT_NEED_RESET:
+                 Driver returns this if it can't recover without a hard
+                 slot reset.
+               - PCI_ERS_RESULT_DISCONNECT:
+                 Driver returns this if it doesn't want to recover at all.
+
+The next step taken will depend on the result codes returned by the
+drivers.
+
+If all drivers on the segment/slot return PCI_ERS_RESULT_CAN_RECOVER,
+then the platform should re-enable IOs on the slot (or do nothing in
+particular, if the platform doesn't isolate slots), and recovery
+proceeds to STEP 2 (MMIO Enable).
+
+If any driver requested a slot reset (by returning PCI_ERS_RESULT_NEED_RESET),
+then recovery proceeds to STEP 4 (Slot Reset).
+
+If the platform is unable to recover the slot, the next step
+is STEP 6 (Permanent Failure).
+
+>>> The current powerpc implementation assumes that a device driver will
+>>> *not* schedule or semaphore in this routine; the current powerpc
 >>> implementation uses one kernel thread to notify all devices;
->>> thus, of one device sleeps/schedules, all devices are affected.
+>>> thus, if one device sleeps/schedules, all devices are affected.
 >>> Doing better requires complex multi-threaded logic in the error
 >>> recovery implementation (e.g. waiting for all notification threads
 >>> to "join" before proceeding with recovery.)  This seems excessively
 >>> complex and not worth implementing.
 
->>> The current ppc64 implementation doesn't much care if the device
->>> attempts i/o at this point, or not.  I/O's will fail, returning
+>>> The current powerpc implementation doesn't much care if the device
+>>> attempts I/O at this point, or not.  I/O's will fail, returning
 >>> a value of 0xff on read, and writes will be dropped. If the device
 >>> driver attempts more than 10K I/O's to a frozen adapter, it will
 >>> assume that the device driver has gone into an infinite loop, and
->>> it will panic the the kernel.
+>>> it will panic the the kernel. There doesn't seem to be any other
+>>> way of stopping a device driver that insists on spinning on I/O.
 
-       2) mmio_enabled()
+STEP 2: MMIO Enabled
+-------------------
+The platform re-enables MMIO to the device (but typically not the
+DMA), and then calls the mmio_enabled() callback on all affected
+device drivers.
 
-       This is the "early recovery" call. IOs are allowed again, but DMA is
+This is the "early recovery" call. IOs are allowed again, but DMA is
 not (hrm... to be discussed, I prefer not), with some restrictions. This
 is NOT a callback for the driver to start operations again, only to
 peek/poke at the device, extract diagnostic information, if any, and
 eventually do things like trigger a device local reset or some such,
-but not restart operations. This is sent if all drivers on a segment
-agree that they can try to recover and no automatic link reset was
-performed by the HW. If the platform can't just re-enable IOs without
-a slot reset or a link reset, it doesn't call this callback and goes
-directly to 3) or 4). All IOs should be done _synchronously_ from
-within this callback, errors triggered by them will be returned via
-the normal pci_check_whatever() api, no new error_detected() callback
-will be issued due to an error happening here. However, such an error
-might cause IOs to be re-blocked for the whole segment, and thus
-invalidate the recovery that other devices on the same segment might
-have done, forcing the whole segment into one of the next states,
-that is link reset or slot reset.
-
-       Result codes:
-               - PCIERR_RESULT_RECOVERED
+but not restart operations. This is callback is made if all drivers on
+a segment agree that they can try to recover and if no automatic link reset
+was performed by the HW. If the platform can't just re-enable IOs without
+a slot reset or a link reset, it wont call this callback, and instead
+will have gone directly to STEP 3 (Link Reset) or STEP 4 (Slot Reset)
+
+>>> The following is proposed; no platform implements this yet:
+>>> Proposal: All I/O's should be done _synchronously_ from within
+>>> this callback, errors triggered by them will be returned via
+>>> the normal pci_check_whatever() API, no new error_detected()
+>>> callback will be issued due to an error happening here. However,
+>>> such an error might cause IOs to be re-blocked for the whole
+>>> segment, and thus invalidate the recovery that other devices
+>>> on the same segment might have done, forcing the whole segment
+>>> into one of the next states, that is, link reset or slot reset.
+
+The driver should return one of the following result codes:
+               - PCI_ERS_RESULT_RECOVERED
                  Driver returns this if it thinks the device is fully
-                 functionnal and thinks it is ready to start
+                 functional and thinks it is ready to start
                  normal driver operations again. There is no
                  guarantee that the driver will actually be
                  allowed to proceed, as another driver on the
                  same segment might have failed and thus triggered a
                  slot reset on platforms that support it.
 
-               - PCIERR_RESULT_NEED_RESET
+               - PCI_ERS_RESULT_NEED_RESET
                  Driver returns this if it thinks the device is not
                  recoverable in it's current state and it needs a slot
                  reset to proceed.
 
-               - PCIERR_RESULT_DISCONNECT
+               - PCI_ERS_RESULT_DISCONNECT
                  Same as above. Total failure, no recovery even after
                  reset driver dead. (To be defined more precisely)
 
->>> The current ppc64 implementation does not implement this callback.
+The next step taken depends on the results returned by the drivers.
+If all drivers returned PCI_ERS_RESULT_RECOVERED, then the platform
+proceeds to either STEP3 (Link Reset) or to STEP 5 (Resume Operations).
+
+If any driver returned PCI_ERS_RESULT_NEED_RESET, then the platform
+proceeds to STEP 4 (Slot Reset)
 
-       3) link_reset()
+>>> The current powerpc implementation does not implement this callback.
 
-       This is called after the link has been reset. This is typically
-a PCI Express specific state at this point and is done whenever a
-non-fatal error has been detected that can be "solved" by resetting
-the link. This call informs the driver of the reset and the driver
-should check if the device appears to be in working condition.
-This function acts a bit like 2) mmio_enabled(), in that the driver
-is not supposed to restart normal driver I/O operations right away.
-Instead, it should just "probe" the device to check it's recoverability
-status. If all is right, then the core will call resume() once all
-drivers have ack'd link_reset().
+
+STEP 3: Link Reset
+------------------
+The platform resets the link, and then calls the link_reset() callback
+on all affected device drivers.  This is a PCI-Express specific state
+and is done whenever a non-fatal error has been detected that can be
+"solved" by resetting the link. This call informs the driver of the
+reset and the driver should check to see if the device appears to be
+in working condition.
+
+The driver is not supposed to restart normal driver I/O operations
+at this point.  It should limit itself to "probing" the device to
+check it's recoverability status. If all is right, then the platform
+will call resume() once all drivers have ack'd link_reset().
 
        Result codes:
-               (identical to mmio_enabled)
+               (identical to STEP 3 (MMIO Enabled)
+
+The platform then proceeds to either STEP 4 (Slot Reset) or STEP 5
+(Resume Operations).
+
+>>> The current powerpc implementation does not implement this callback.
+
+
+STEP 4: Slot Reset
+------------------
+The platform performs a soft or hard reset of the device, and then
+calls the slot_reset() callback.
+
+A soft reset consists of asserting the adapter #RST line and then
+restoring the PCI BAR's and PCI configuration header to a state
+that is equivalent to what it would be after a fresh system
+power-on followed by power-on BIOS/system firmware initialization.
+If the platform supports PCI hotplug, then the reset might be
+performed by toggling the slot electrical power off/on.
 
->>> The current ppc64 implementation does not implement this callback.
+It is important for the platform to restore the PCI config space
+to the "fresh poweron" state, rather than the "last state". After
+a slot reset, the device driver will almost always use its standard
+device initialization routines, and an unusual config space setup
+may result in hung devices, kernel panics, or silent data corruption.
 
-       4) slot_reset()
+This call gives drivers the chance to re-initialize the hardware
+(re-download firmware, etc.).  At this point, the driver may assume
+that he card is in a fresh state and is fully functional. In
+particular, interrupt generation should work normally.
 
-       This is called after the slot has been soft or hard reset by the
-platform.  A soft reset consists of asserting the adapter #RST line
-and then restoring the PCI BARs and PCI configuration header. If the
-platform supports PCI hotplug, then it might instead perform a hard
-reset by toggling power on the slot off/on. This call gives drivers
-the chance to re-initialize the hardware (re-download firmware, etc.),
-but drivers shouldn't restart normal I/O processing operations at
-this point.  (See note about interrupts; interrupts aren't guaranteed
-to be delivered until the resume() callback has been called). If all
-device drivers report success on this callback, the patform will call
-resume() to complete the error handling and let the driver restart
-normal I/O processing.
+Drivers should not yet restart normal I/O processing operations
+at this point.  If all device drivers report success on this
+callback, the platform will call resume() to complete the sequence,
+and let the driver restart normal I/O processing.
 
 A driver can still return a critical failure for this function if
 it can't get the device operational after reset.  If the platform
-previously tried a soft reset, it migh now try a hard reset (power
+previously tried a soft reset, it might now try a hard reset (power
 cycle) and then call slot_reset() again.  It the device still can't
 be recovered, there is nothing more that can be done;  the platform
 will typically report a "permanent failure" in such a case.  The
 device will be considered "dead" in this case.
 
-       Result codes:
-               - PCIERR_RESULT_DISCONNECT
-               Same as above.
+Drivers for multi-function cards will need to coordinate among
+themselves as to which driver instance will perform any "one-shot"
+or global device initialization. For example, the Symbios sym53cxx2
+driver performs device init only from PCI function 0:
 
->>> The current ppc64 implementation does not try a power-cycle reset
->>> if the driver returned PCIERR_RESULT_DISCONNECT. However, it should.
++       if (PCI_FUNC(pdev->devfn) == 0)
++               sym_reset_scsi_bus(np, 0);
 
-       5) resume()
-
-       This is called if all drivers on the segment have returned
-PCIERR_RESULT_RECOVERED from one of the 3 prevous callbacks.
-That basically tells the driver to restart activity, tht everything
-is back and running. No result code is taken into account here. If
-a new error happens, it will restart a new error handling process.
+       Result codes:
+               - PCI_ERS_RESULT_DISCONNECT
+               Same as above.
 
-That's it. I think this covers all the possibilities. The way those
-callbacks are called is platform policy. A platform with no slot reset
-capability for example may want to just "ignore" drivers that can't
+Platform proceeds either to STEP 5 (Resume Operations) or STEP 6 (Permanent
+Failure).
+
+>>> The current powerpc implementation does not currently try a
+>>> power-cycle reset if the driver returned PCI_ERS_RESULT_DISCONNECT.
+>>> However, it probably should.
+
+
+STEP 5: Resume Operations
+-------------------------
+The platform will call the resume() callback on all affected device
+drivers if all drivers on the segment have returned
+PCI_ERS_RESULT_RECOVERED from one of the 3 previous callbacks.
+The goal of this callback is to tell the driver to restart activity,
+that everything is back and running. This callback does not return
+a result code.
+
+At this point, if a new error happens, the platform will restart
+a new error recovery sequence.
+
+STEP 6: Permanent Failure
+-------------------------
+A "permanent failure" has occurred, and the platform cannot recover
+the device.  The platform will call error_detected() with a
+pci_channel_state value of pci_channel_io_perm_failure.
+
+The device driver should, at this point, assume the worst. It should
+cancel all pending I/O, refuse all new I/O, returning -EIO to
+higher layers. The device driver should then clean up all of its
+memory and remove itself from kernel operations, much as it would
+during system shutdown.
+
+The platform will typically notify the system operator of the
+permanent failure in some way.  If the device is hotplug-capable,
+the operator will probably want to remove and replace the device.
+Note, however, not all failures are truly "permanent". Some are
+caused by over-heating, some by a poorly seated card. Many
+PCI error events are caused by software bugs, e.g. DMA's to
+wild addresses or bogus split transactions due to programming
+errors. See the discussion in powerpc/eeh-pci-error-recovery.txt
+for additional detail on real-life experience of the causes of
+software errors.
+
+
+Conclusion; General Remarks
+---------------------------
+The way those callbacks are called is platform policy. A platform with
+no slot reset capability may want to just "ignore" drivers that can't
 recover (disconnect them) and try to let other cards on the same segment
 recover. Keep in mind that in most real life cases, though, there will
 be only one driver per segment.
 
-Now, there is a note about interrupts. If you get an interrupt and your
+Now, a note about interrupts. If you get an interrupt and your
 device is dead or has been isolated, there is a problem :)
-
-After much thinking, I decided to leave that to the platform. That is,
-the recovery API only precies that:
+The current policy is to turn this into a platform policy.
+That is, the recovery API only requires that:
 
  - There is no guarantee that interrupt delivery can proceed from any
 device on the segment starting from the error detection and until the
-restart callback is sent, at which point interrupts are expected to be
+resume callback is sent, at which point interrupts are expected to be
 fully operational.
 
- - There is no guarantee that interrupt delivery is stopped, that is, ad
-river that gets an interrupts after detecting an error, or that detects
-and error within the interrupt handler such that it prevents proper
+ - There is no guarantee that interrupt delivery is stopped, that is,
+a driver that gets an interrupt after detecting an error, or that detects
+an error within the interrupt handler such that it prevents proper
 ack'ing of the interrupt (and thus removal of the source) should just
-return IRQ_NOTHANDLED. It's up to the platform to deal with taht
-condition, typically by masking the irq source during the duration of
+return IRQ_NOTHANDLED. It's up to the platform to deal with that
+condition, typically by masking the IRQ source during the duration of
 the error handling. It is expected that the platform "knows" which
 interrupts are routed to error-management capable slots and can deal
-with temporarily disabling that irq number during error processing (this
+with temporarily disabling that IRQ number during error processing (this
 isn't terribly complex). That means some IRQ latency for other devices
 sharing the interrupt, but there is simply no other way. High end
 platforms aren't supposed to share interrupts between many devices
 anyway :)
 
-
-Revised: 31 May 2005 Linas Vepstas <linas@austin.ibm.com>
+>>> Implementation details for the powerpc platform are discussed in
+>>> the file Documentation/powerpc/eeh-pci-error-recovery.txt
+
+>>> As of this writing, there are six device drivers with patches
+>>> implementing error recovery. Not all of these patches are in
+>>> mainline yet. These may be used as "examples":
+>>>
+>>> drivers/scsi/ipr.c
+>>> drivers/scsi/sym53cxx_2
+>>> drivers/next/e100.c
+>>> drivers/net/e1000
+>>> drivers/net/ixgb
+>>> drivers/net/s2io.c
+
+The End
+-------
index 42955fe..e384eb7 100644 (file)
@@ -1984,7 +1984,6 @@ M:        philb@gnu.org
 P:     Tim Waugh
 M:     tim@cyberelk.net
 P:     David Campbell
-M:     campbell@torque.net
 P:     Andrea Arcangeli
 M:     andrea@suse.de
 L:     linux-parport@lists.infradead.org
index 252a659..cd5b619 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 16
-EXTRAVERSION =-rc1
+EXTRAVERSION =-rc2
 NAME=Sliding Snow Leopard
 
 # *DOCUMENTATION*
index 21654be..acc1813 100644 (file)
@@ -49,7 +49,9 @@ dump_backtrace(struct frame_head * head)
  * |    stack    |
  * --------------- saved regs->ebp value if valid (frame_head address)
  * .             .
- * --------------- struct pt_regs stored on stack (struct pt_regs *)
+ * --------------- saved regs->rsp value if x86_64
+ * |             |
+ * --------------- struct pt_regs * stored on stack if 32-bit
  * |             |
  * .             .
  * |             |
@@ -57,13 +59,26 @@ dump_backtrace(struct frame_head * head)
  * |             |
  * |             | \/ Lower addresses
  *
- * Thus, &pt_regs <-> stack base restricts the valid(ish) ebp values
+ * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the
+ * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other
+ * exceptions use special stacks, maintained by the interrupt stack table
+ * (IST). These stacks are set up in trap_init() in
+ * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point
+ * to the kernel stack; instead, it points to some location on the NMI
+ * stack. On the other hand, regs->rsp is the stack pointer saved when the
+ * NMI occurred. (2) For 32-bit, regs->esp is not valid because the
+ * processor does not save %esp on the kernel stack when interrupts occur
+ * in the kernel mode.
  */
 #ifdef CONFIG_FRAME_POINTER
 static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs)
 {
        unsigned long headaddr = (unsigned long)head;
+#ifdef CONFIG_X86_64
+       unsigned long stack = (unsigned long)regs->rsp;
+#else
        unsigned long stack = (unsigned long)regs;
+#endif
        unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
 
        return headaddr > stack && headaddr < stack_base;
index 706b773..6e5eea1 100644 (file)
@@ -71,31 +71,33 @@ static int __init topology_init(void)
        int i, err = 0;
 
 #ifdef CONFIG_NUMA
-       sysfs_nodes = kmalloc(sizeof(struct node) * MAX_NUMNODES, GFP_KERNEL);
+       sysfs_nodes = kzalloc(sizeof(struct node) * MAX_NUMNODES, GFP_KERNEL);
        if (!sysfs_nodes) {
                err = -ENOMEM;
                goto out;
        }
-       memset(sysfs_nodes, 0, sizeof(struct node) * MAX_NUMNODES);
 
-       /* MCD - Do we want to register all ONLINE nodes, or all POSSIBLE nodes? */
-       for_each_online_node(i)
+       /*
+        * MCD - Do we want to register all ONLINE nodes, or all POSSIBLE nodes?
+        */
+       for_each_online_node(i) {
                if ((err = register_node(&sysfs_nodes[i], i, 0)))
                        goto out;
+       }
 #endif
 
-       sysfs_cpus = kmalloc(sizeof(struct ia64_cpu) * NR_CPUS, GFP_KERNEL);
+       sysfs_cpus = kzalloc(sizeof(struct ia64_cpu) * NR_CPUS, GFP_KERNEL);
        if (!sysfs_cpus) {
                err = -ENOMEM;
                goto out;
        }
-       memset(sysfs_cpus, 0, sizeof(struct ia64_cpu) * NR_CPUS);
 
-       for_each_present_cpu(i)
+       for_each_present_cpu(i) {
                if((err = arch_register_cpu(i)))
                        goto out;
+       }
 out:
        return err;
 }
 
-__initcall(topology_init);
+subsys_initcall(topology_init);
index 6e27ac6..83b33fe 100644 (file)
@@ -1486,7 +1486,7 @@ sys_inotify_rm_watch_wrapper:
 
        .globl compat_sys_openat_wrapper
 compat_sys_openat_wrapper:
-       lgfr    %r2,%r2                 # int
+       llgfr   %r2,%r2                 # unsigned int
        llgtr   %r3,%r3                 # const char *
        lgfr    %r4,%r4                 # int
        lgfr    %r5,%r5                 # int
@@ -1518,14 +1518,14 @@ sys_fchownat_wrapper:
 
        .globl compat_sys_futimesat_wrapper
 compat_sys_futimesat_wrapper:
-       lgfr    %r2,%r2                 # int
+       llgfr   %r2,%r2                 # unsigned int
        llgtr   %r3,%r3                 # char *
        llgtr   %r4,%r4                 # struct timeval *
        jg      compat_sys_futimesat
 
        .globl compat_sys_newfstatat_wrapper
 compat_sys_newfstatat_wrapper:
-       lgfr    %r2,%r2                 # int
+       llgfr   %r2,%r2                 # unsigned int
        llgtr   %r3,%r3                 # char *
        llgtr   %r4,%r4                 # struct stat *
        lgfr    %r5,%r5                 # int
index 7f0efaa..3975aa0 100644 (file)
@@ -117,6 +117,7 @@ late_initcall(simcons_tty_init);
    tty driver.  */
 void simcons_poll_tty (struct tty_struct *tty)
 {
+       char buf[32];   /* Not the nicest way to do it but I need it correct first */
        int flip = 0, send_break = 0;
        struct pollfd pfd;
        pfd.fd = 0;
@@ -124,21 +125,15 @@ void simcons_poll_tty (struct tty_struct *tty)
 
        if (V850_SIM_SYSCALL (poll, &pfd, 1, 0) > 0) {
                if (pfd.revents & POLLIN) {
-                       int left = TTY_FLIPBUF_SIZE - tty->flip.count;
-
-                       if (left > 0) {
-                               unsigned char *buf = tty->flip.char_buf_ptr;
-                               int rd = V850_SIM_SYSCALL (read, 0, buf, left);
-
-                               if (rd > 0) {
-                                       tty->flip.count += rd;
-                                       tty->flip.char_buf_ptr += rd;
-                                       memset (tty->flip.flag_buf_ptr, 0, rd);
-                                       tty->flip.flag_buf_ptr += rd;
-                                       flip = 1;
-                               } else
-                                       send_break = 1;
-                       }
+                       /* Real block hardware knows the transfer size before
+                          transfer so the new tty buffering doesn't try to handle
+                          this rather weird simulator specific case well */
+                       int rd = V850_SIM_SYSCALL (read, 0, buf, 32);
+                       if (rd > 0) {
+                               tty_insert_flip_string(tty, buf, rd);
+                               flip = 1;
+                       } else
+                               send_break = 1;
                } else if (pfd.revents & POLLERR)
                        send_break = 1;
        }
index 4fbddf9..94fdfe4 100644 (file)
@@ -128,9 +128,7 @@ static void rs_poll(unsigned long priv)
 
        while (__simc(SYS_select_one, 0, XTISS_SELECT_ONE_READ, (int)&tv,0,0)){
                __simc (SYS_read, 0, (unsigned long)&c, 1, 0, 0);
-               tty->flip.count++;
-               *tty->flip.char_buf_ptr++ = c;
-               *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+               tty_insert_flip_char(tty, c, TTY_NORMAL);
                i++;
        }
 
index f12898d..e99471d 100644 (file)
@@ -8,6 +8,7 @@ obj-y                   += power/
 obj-$(CONFIG_FW_LOADER)        += firmware_class.o
 obj-$(CONFIG_NUMA)     += node.o
 obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
+obj-$(CONFIG_SMP)      += topology.o
 
 ifeq ($(CONFIG_DEBUG_DRIVER),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
new file mode 100644 (file)
index 0000000..915810f
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * driver/base/topology.c - Populate sysfs with cpu topology information
+ *
+ * Written by: Zhang Yanmin, Intel Corporation
+ *
+ * Copyright (C) 2006, Intel Corp.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/sysdev.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+
+#define define_one_ro(_name)           \
+static SYSDEV_ATTR(_name, 0444, show_##_name, NULL)
+
+#define define_id_show_func(name)                              \
+static ssize_t show_##name(struct sys_device *dev, char *buf)  \
+{                                                              \
+       unsigned int cpu = dev->id;                             \
+       return sprintf(buf, "%d\n", topology_##name(cpu));      \
+}
+
+#define define_siblings_show_func(name)                                        \
+static ssize_t show_##name(struct sys_device *dev, char *buf)          \
+{                                                                      \
+       ssize_t len = -1;                                               \
+       unsigned int cpu = dev->id;                                     \
+       len = cpumask_scnprintf(buf, NR_CPUS+1, topology_##name(cpu));  \
+       return (len + sprintf(buf + len, "\n"));                        \
+}
+
+#ifdef topology_physical_package_id
+define_id_show_func(physical_package_id);
+define_one_ro(physical_package_id);
+#define ref_physical_package_id_attr   &attr_physical_package_id.attr,
+#else
+#define ref_physical_package_id_attr
+#endif
+
+#ifdef topology_core_id
+define_id_show_func(core_id);
+define_one_ro(core_id);
+#define ref_core_id_attr               &attr_core_id.attr,
+#else
+#define ref_core_id_attr
+#endif
+
+#ifdef topology_thread_siblings
+define_siblings_show_func(thread_siblings);
+define_one_ro(thread_siblings);
+#define ref_thread_siblings_attr       &attr_thread_siblings.attr,
+#else
+#define ref_thread_siblings_attr
+#endif
+
+#ifdef topology_core_siblings
+define_siblings_show_func(core_siblings);
+define_one_ro(core_siblings);
+#define ref_core_siblings_attr         &attr_core_siblings.attr,
+#else
+#define ref_core_siblings_attr
+#endif
+
+static struct attribute *default_attrs[] = {
+       ref_physical_package_id_attr
+       ref_core_id_attr
+       ref_thread_siblings_attr
+       ref_core_siblings_attr
+       NULL
+};
+
+static struct attribute_group topology_attr_group = {
+       .attrs = default_attrs,
+       .name = "topology"
+};
+
+/* Add/Remove cpu_topology interface for CPU device */
+static int __cpuinit topology_add_dev(struct sys_device * sys_dev)
+{
+       sysfs_create_group(&sys_dev->kobj, &topology_attr_group);
+       return 0;
+}
+
+static int __cpuinit topology_remove_dev(struct sys_device * sys_dev)
+{
+       sysfs_remove_group(&sys_dev->kobj, &topology_attr_group);
+       return 0;
+}
+
+static int __cpuinit topology_cpu_callback(struct notifier_block *nfb,
+               unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+       struct sys_device *sys_dev;
+
+       sys_dev = get_cpu_sysdev(cpu);
+       switch (action) {
+       case CPU_ONLINE:
+               topology_add_dev(sys_dev);
+               break;
+       case CPU_DEAD:
+               topology_remove_dev(sys_dev);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block topology_cpu_notifier =
+{
+       .notifier_call = topology_cpu_callback,
+};
+
+static int __cpuinit topology_sysfs_init(void)
+{
+       int i;
+
+       for_each_online_cpu(i) {
+               topology_cpu_callback(&topology_cpu_notifier, CPU_ONLINE,
+                               (void *)(long)i);
+       }
+
+       register_cpu_notifier(&topology_cpu_notifier);
+
+       return 0;
+}
+
+device_initcall(topology_sysfs_init);
+
index a3614e6..4ada126 100644 (file)
@@ -882,7 +882,7 @@ static int __devinit mm_pci_probe(struct pci_dev *dev, const struct pci_device_i
               card->card_number, dev->bus->number, dev->devfn);
 
        if (pci_set_dma_mask(dev, 0xffffffffffffffffLL) &&
-           !pci_set_dma_mask(dev, 0xffffffffLL)) {
+           pci_set_dma_mask(dev, 0xffffffffLL)) {
                printk(KERN_WARNING "MM%d: NO suitable DMA found\n",num_cards);
                return  -ENOMEM;
        }
index 39c61a7..cc7acf8 100644 (file)
@@ -1233,7 +1233,7 @@ cyy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                             }
                              info->idle_stats.recv_idle = jiffies;
                         }
-                        schedule_delayed_work(&tty->buf.work, 1);
+                       tty_schedule_flip(tty);
                     }
                     /* end of service */
                     cy_writeb(base_addr+(CyRIR<<index), (save_xir & 0x3f));
@@ -1606,7 +1606,7 @@ cyz_handle_rx(struct cyclades_port *info,
            }
 #endif
            info->idle_stats.recv_idle = jiffies;
-           schedule_delayed_work(&tty->buf.work, 1);
+           tty_schedule_flip(tty);
        }
        /* Update rx_get */
        cy_writel(&buf_ctrl->rx_get, new_rx_get);
@@ -1809,7 +1809,7 @@ cyz_handle_cmd(struct cyclades_card *cinfo)
        if(delta_count)
            cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
        if(special_count)
-           schedule_delayed_work(&tty->buf.work, 1);
+           tty_schedule_flip(tty);
     }
 }
 
index 3f3ac03..57539d8 100644 (file)
@@ -359,7 +359,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes)
                }
        }
 
-       schedule_delayed_work(&tty->buf.work, 1);
+       tty_schedule_flip(tty);
 
        info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
        release_pio_buffer(pio_buf);
@@ -426,7 +426,7 @@ static inline void receive_chars_dma_done(struct esp_struct *info,
                        }
                        tty_insert_flip_char(tty, dma_buffer[num_bytes - 1], statflag);
                }
-               schedule_delayed_work(&tty->buf.work, 1);
+               tty_schedule_flip(tty);
        }
 
        if (dma_bytes != num_bytes) {
index cb8f419..e7af647 100644 (file)
@@ -139,7 +139,6 @@ static UCHAR ct79[] = { 2, BYP,     0x4F,0                   }; // XMIT_NOW
 //static UCHAR ct86[]={ 2, BTH,     0x56,0                   }; // RCV_ENABLE
 static UCHAR ct87[] = { 1, BYP,     0x57                     }; // HW_TEST
 //static UCHAR ct88[]={ 3, BTH,     0x58,0,0                 }; // RCV_THRESHOLD
-static UCHAR ct89[]={ 1, BYP,     0x59                     }; // DSS_NOW
 //static UCHAR ct90[]={ 3, BYP,     0x5A,0,0                 }; // Set SILO
 //static UCHAR ct91[]={ 2, BYP,     0x5B,0                   }; // timed break
 
index 56e93a5..48fcfba 100644 (file)
@@ -2906,65 +2906,16 @@ ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg )
                rc = -EINVAL;
                break;
        case 3:     // Trace device
-               if ( cmd == 1 ) {
-                       rc = put_user(iiSendPendingMail, pIndex++ );
-                       rc = put_user(i2InitChannels, pIndex++ );
-                       rc = put_user(i2QueueNeeds, pIndex++ );
-                       rc = put_user(i2QueueCommands, pIndex++ );
-                       rc = put_user(i2GetStatus, pIndex++ );
-                       rc = put_user(i2Input, pIndex++ );
-                       rc = put_user(i2InputFlush, pIndex++ );
-                       rc = put_user(i2Output, pIndex++ );
-                       rc = put_user(i2FlushOutput, pIndex++ );
-                       rc = put_user(i2DrainWakeup, pIndex++ );
-                       rc = put_user(i2DrainOutput, pIndex++ );
-                       rc = put_user(i2OutputFree, pIndex++ );
-                       rc = put_user(i2StripFifo, pIndex++ );
-                       rc = put_user(i2StuffFifoBypass, pIndex++ );
-                       rc = put_user(i2StuffFifoFlow, pIndex++ );
-                       rc = put_user(i2StuffFifoInline, pIndex++ );
-                       rc = put_user(i2ServiceBoard, pIndex++ );
-                       rc = put_user(serviceOutgoingFifo, pIndex++ );
-                       // rc = put_user(ip2_init, pIndex++ );
-                       rc = put_user(ip2_init_board, pIndex++ );
-                       rc = put_user(find_eisa_board, pIndex++ );
-                       rc = put_user(set_irq, pIndex++ );
-                       rc = put_user(ip2_interrupt, pIndex++ );
-                       rc = put_user(ip2_poll, pIndex++ );
-                       rc = put_user(service_all_boards, pIndex++ );
-                       rc = put_user(do_input, pIndex++ );
-                       rc = put_user(do_status, pIndex++ );
-#ifndef IP2DEBUG_OPEN
-                       rc = put_user(0, pIndex++ );
-#else
-                       rc = put_user(open_sanity_check, pIndex++ );
-#endif
-                       rc = put_user(ip2_open, pIndex++ );
-                       rc = put_user(ip2_close, pIndex++ );
-                       rc = put_user(ip2_hangup, pIndex++ );
-                       rc = put_user(ip2_write, pIndex++ );
-                       rc = put_user(ip2_putchar, pIndex++ );
-                       rc = put_user(ip2_flush_chars, pIndex++ );
-                       rc = put_user(ip2_write_room, pIndex++ );
-                       rc = put_user(ip2_chars_in_buf, pIndex++ );
-                       rc = put_user(ip2_flush_buffer, pIndex++ );
-
-                       //rc = put_user(ip2_wait_until_sent, pIndex++ );
-                       rc = put_user(0, pIndex++ );
-
-                       rc = put_user(ip2_throttle, pIndex++ );
-                       rc = put_user(ip2_unthrottle, pIndex++ );
-                       rc = put_user(ip2_ioctl, pIndex++ );
-                       rc = put_user(0, pIndex++ );
-                       rc = put_user(get_serial_info, pIndex++ );
-                       rc = put_user(set_serial_info, pIndex++ );
-                       rc = put_user(ip2_set_termios, pIndex++ );
-                       rc = put_user(ip2_set_line_discipline, pIndex++ );
-                       rc = put_user(set_params, pIndex++ );
-               } else {
+               /*
+                * akpm: This used to write a whole bunch of function addresses
+                * to userspace, which generated lots of put_user() warnings.
+                * I killed it all.  Just return "success" and don't do
+                * anything.
+                */
+               if (cmd == 1)
+                       rc = 0;
+               else
                        rc = -EINVAL;
-               }
-
                break;
 
        default:
index 0097f06..d745004 100644 (file)
@@ -481,7 +481,7 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len)
        }
 
        if ((addr->channel == IPMI_BMC_CHANNEL)
-           || (addr->channel >= IPMI_NUM_CHANNELS)
+           || (addr->channel >= IPMI_MAX_CHANNELS)
            || (addr->channel < 0))
                return -EINVAL;
 
@@ -1321,7 +1321,7 @@ static int i_ipmi_request(ipmi_user_t          user,
                unsigned char         ipmb_seq;
                long                  seqid;
 
-               if (addr->channel >= IPMI_NUM_CHANNELS) {
+               if (addr->channel >= IPMI_MAX_CHANNELS) {
                        spin_lock_irqsave(&intf->counter_lock, flags);
                        intf->sent_invalid_commands++;
                        spin_unlock_irqrestore(&intf->counter_lock, flags);
index 6ed213b..e59b638 100644 (file)
@@ -1270,36 +1270,36 @@ static int try_init_port(int intf_num, struct smi_info **new_info)
        return 0;
 }
 
-static unsigned char mem_inb(struct si_sm_io *io, unsigned int offset)
+static unsigned char intf_mem_inb(struct si_sm_io *io, unsigned int offset)
 {
        return readb((io->addr)+(offset * io->regspacing));
 }
 
-static void mem_outb(struct si_sm_io *io, unsigned int offset,
+static void intf_mem_outb(struct si_sm_io *io, unsigned int offset,
                     unsigned char b)
 {
        writeb(b, (io->addr)+(offset * io->regspacing));
 }
 
-static unsigned char mem_inw(struct si_sm_io *io, unsigned int offset)
+static unsigned char intf_mem_inw(struct si_sm_io *io, unsigned int offset)
 {
        return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
                && 0xff;
 }
 
-static void mem_outw(struct si_sm_io *io, unsigned int offset,
+static void intf_mem_outw(struct si_sm_io *io, unsigned int offset,
                     unsigned char b)
 {
        writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
 }
 
-static unsigned char mem_inl(struct si_sm_io *io, unsigned int offset)
+static unsigned char intf_mem_inl(struct si_sm_io *io, unsigned int offset)
 {
        return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
                && 0xff;
 }
 
-static void mem_outl(struct si_sm_io *io, unsigned int offset,
+static void intf_mem_outl(struct si_sm_io *io, unsigned int offset,
                     unsigned char b)
 {
        writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
@@ -1349,16 +1349,16 @@ static int mem_setup(struct smi_info *info)
           upon the register size. */
        switch (info->io.regsize) {
        case 1:
-               info->io.inputb = mem_inb;
-               info->io.outputb = mem_outb;
+               info->io.inputb = intf_mem_inb;
+               info->io.outputb = intf_mem_outb;
                break;
        case 2:
-               info->io.inputb = mem_inw;
-               info->io.outputb = mem_outw;
+               info->io.inputb = intf_mem_inw;
+               info->io.outputb = intf_mem_outw;
                break;
        case 4:
-               info->io.inputb = mem_inl;
-               info->io.outputb = mem_outl;
+               info->io.inputb = intf_mem_inl;
+               info->io.outputb = intf_mem_outl;
                break;
 #ifdef readq
        case 8:
index 217ff09..89bd94e 100644 (file)
 #endif
 #define _cirrus_h 1
 
-#ifdef RTA
-#define        TO_UART RX
-#define TO_DRIVER TX
-#endif
-
-#ifdef HOST
-#define        TO_UART TX
-#define TO_DRIVER RX
-#endif
-#ifdef RTA
-/* Miscellaneous defines for CIRRUS addresses and related logic for
-   interrupts etc.
-*/
-#define        MAP(a)          ((short *)(cirrus_base + (a)))
-#define outp(a,b)      (*MAP (a) =(b))
-#define inp(a)         ((*MAP (a)) & 0xff)
-#define        CIRRUS_FIRST    (short*)0x7300
-#define        CIRRUS_SECOND   (short*)0x7200
-#define        CIRRUS_THIRD    (short*)0x7100
-#define        CIRRUS_FOURTH   (short*)0x7000
-#define        PORTS_ON_CIRRUS 4
-#define        CIRRUS_FIFO_SIZE        12
-#define        SPACE           0x20
-#define        TAB             0x09
-#define        LINE_FEED       0x0a
-#define        CARRIAGE_RETURN 0x0d
-#define        BACKSPACE       0x08
-#define        SPACES_IN_TABS  8
-#define        SEND_ESCAPE     0x00
-#define START_BREAK    0x81
-#define        TIMER_TICK      0x82
-#define STOP_BREAK     0x83
-#define BASE(a) ((a) < 4 ? (short*)CIRRUS_FIRST : ((a) < 8 ? (short *)CIRRUS_SECOND : ((a) < 12 ? (short*)CIRRUS_THIRD : (short *)CIRRUS_FOURTH)))
-#define txack1 ((short *)0x7104)
-#define rxack1 ((short *)0x7102)
-#define mdack1  ((short *)0x7106)
-#define txack2  ((short *)0x7006)
-#define rxack2 ((short *)0x7004)
-#define mdack2  ((short *)0x7100)
-#define int_latch       ((short *) 0x7800)
-#define int_status      ((short *) 0x7c00)
-#define tx1_pending     0x20
-#define rx1_pending     0x10
-#define md1_pending     0x40
-#define tx2_pending     0x02
-#define rx2_pending     0x01
-#define md2_pending     0x40
-#define module1_bits   0x07
-#define module1_modern 0x08
-#define module2_bits   0x70
-#define module2_modern 0x80
-#define module_blank   0xf
-#define rs232_d25      0x0
-#define        rs232_rj45      0x1
-#define rs422_d25      0x3
-#define parallel       0x5
-
-#define        CLK0    0x00
-#define CLK1   0x01
-#define CLK2   0x02
-#define CLK3   0x03
-#define CLK4   0x04
-
-#define CIRRUS_REVC    0x42
-#define CIRRUS_REVE    0x44
-
-#define        TURNON  1
-#define TURNOFF 0
-
-/* The list of CIRRUS registers. 
-   NB. These registers are relative values on 8 bit boundaries whereas
-   on the RTA's the CIRRUS registers are on word boundaries. Use pointer
-   arithmetic (short *) to obtain the real addresses required */
-#define ccr    0x05            /* Channel Command Register     */
-#define ier    0x06            /* Interrupt Enable Register    */
-#define cor1   0x08            /* Channel Option Register 1    */
-#define cor2   0x09            /* Channel Option Register 2    */
-#define cor3   0x0a            /* Channel Option Register 3    */
-#define cor4   0x1e            /* Channel Option Register 4    */
-#define        cor5    0x1f            /* Channel Option Register 5    */
-
-#define ccsr   0x0b            /* Channel Control Status Register */
-#define rdcr   0x0e            /* Receive Data Count Register  */
-#define tdcr   0x12            /* Transmit Data Count Register */
-#define mcor1  0x15            /* Modem Change Option Register 1 */
-#define mcor2  0x16            /* Modem Change Option Regsiter 2 */
-
-#define livr   0x18            /* Local Interrupt Vector Register */
-#define schr1  0x1a            /* Special Character Register 1 */
-#define schr2  0x1b            /* Special Character Register 2 */
-#define schr3  0x1c            /* Special Character Register 3 */
-#define schr4  0x1d            /* Special Character Register 4 */
-
-#define rtr    0x20            /* Receive Timer Register */
-#define rtpr   0x21            /* Receive Timeout Period Register */
-#define lnc    0x24            /* Lnext character */
-
-#define rivr   0x43            /* Receive Interrupt Vector Register    */
-#define tivr   0x42            /* Transmit Interrupt Vector Register   */
-#define mivr   0x41            /* Modem Interrupt Vector Register      */
-#define gfrcr  0x40            /* Global Firmware Revision code Reg    */
-#define ricr   0x44            /* Receive Interrupting Channel Reg     */
-#define ticr   0x45            /* Transmit Interrupting Channel Reg    */
-#define micr   0x46            /* Modem Interrupting Channel Register  */
-
-#define gcr    0x4b            /* Global configuration register */
-#define misr    0x4c           /* Modem interrupt status register */
-
-#define rbusr  0x59
-#define tbusr  0x5a
-#define mbusr  0x5b
-
-#define eoir   0x60            /* End Of Interrupt Register */
-#define rdsr   0x62            /* Receive Data / Status Register */
-#define tdr    0x63            /* Transmit Data Register */
-#define svrr   0x67            /* Service Request Register */
-
-#define car    0x68            /* Channel Access Register */
-#define mir    0x69            /* Modem Interrupt Register */
-#define tir    0x6a            /* Transmit Interrupt Register */
-#define rir    0x6b            /* Receive Interrupt Register */
-#define msvr1  0x6c            /* Modem Signal Value Register 1 */
-#define msvr2  0x6d            /* Modem Signal Value Register 2 */
-#define psvr   0x6f            /* Printer Signal Value Register */
-
-#define tbpr   0x72            /* Transmit Baud Rate Period Register */
-#define tcor   0x76            /* Transmit Clock Option Register */
-
-#define rbpr   0x78            /* Receive Baud Rate Period Register */
-#define rber   0x7a            /* Receive Baud Rate Extension Register */
-#define rcor   0x7c            /* Receive Clock Option Register */
-#define ppr    0x7e            /* Prescalar Period Register    */
-
-/* Misc registers used for forcing the 1400 out of its reset woes */
-#define airl   0x6d
-#define airm   0x6e
-#define airh   0x6f
-#define btcr   0x66
-#define mtcr   0x6c
-#define tber   0x74
-
-#endif                         /* #ifdef RTA */
 
 
 /* Bit fields for particular registers */
index 5b600c3..d55c2f6 100644 (file)
@@ -45,13 +45,6 @@ static char *_rio_defaults_h_sccs = "@(#)defaults.h  1.1";
 #define MILLISECOND           (int) (1000/64)  /* 15.625 low ticks */
 #define SECOND                (int) 15625      /* Low priority ticks */
 
-#ifdef RTA
-#define RX_LIMIT       (ushort) 3
-#endif
-#ifdef HOST
-#define RX_LIMIT       (ushort) 1
-#endif
-
 #define LINK_TIMEOUT          (int) (POLL_PERIOD / 2)
 
 
index bfba5b0..48d68ca 100644 (file)
 /*
 ** LED stuff
 */
-#if defined(RTA)
-#define LED_OFF            ((ushort) 0)        /* LED off */
-#define LED_RED            ((ushort) 1)        /* LED Red */
-#define LED_GREEN          ((ushort) 2)        /* LED Green */
-#define LED_ORANGE         ((ushort) 4)        /* LED Orange */
-#define LED_1TO8_OPEN      ((ushort) 1)        /* Port 1->8 LED on */
-#define LED_9TO16_OPEN     ((ushort) 2)        /* Port 9->16 LED on */
-#define LED_SET_COLOUR(colour) (link->led = (colour))
-#define LED_OR_COLOUR(colour)  (link->led |= (colour))
-#define LED_TIMEOUT(time)    (link->led_timeout = RioTimePlus(RioTime(),(time)))
-#else
 #define LED_SET_COLOUR(colour)
 #define LED_OR_COLOUR(colour)
 #define LED_TIMEOUT(time)
-#endif                         /* RTA */
 
 struct LPB {
        WORD link_number;       /* Link Number */
        Channel_ptr in_ch;      /* Link In Channel */
        Channel_ptr out_ch;     /* Link Out Channel */
-#ifdef RTA
-       uchar stat_led;         /* Port open leds */
-       uchar led;              /* True, light led! */
-#endif
        BYTE attached_serial[4];        /* Attached serial number */
        BYTE attached_host_serial[4];
        /* Serial number of Host who
@@ -144,30 +128,12 @@ struct LPB {
        WORD WaitNoBoot;        /* Secs to hold off booting */
        PKT_ptr add_packet_list;        /* Add packets to here */
        PKT_ptr remove_packet_list;     /* Send packets from here */
-#ifdef RTA
-#ifdef DCIRRUS
-#define    QBUFS_PER_REDIRECT (4 / PKTS_PER_BUFFER + 1)
-#else
-#define    QBUFS_PER_REDIRECT (8 / PKTS_PER_BUFFER + 1)
-#endif
-       PKT_ptr_ptr rd_add;     /* Add a new Packet here */
-       Q_BUF_ptr rd_add_qb;    /* Pointer to the add Q buf */
-       PKT_ptr_ptr rd_add_st_qbb;      /* Pointer to start of the Q's buf */
-       PKT_ptr_ptr rd_add_end_qbb;     /* Pointer to the end of the Q's buf */
-       PKT_ptr_ptr rd_remove;  /* Remove a Packet here */
-       Q_BUF_ptr rd_remove_qb; /* Pointer to the remove Q buf */
-       PKT_ptr_ptr rd_remove_st_qbb;   /* Pointer to the start of the Q buf */
-       PKT_ptr_ptr rd_remove_end_qbb;  /* Pointer to the end of the Q buf */
-       ushort pkts_in_q;       /* Packets in queue */
-#endif
 
        Channel_ptr lrt_fail_chan;      /* Lrt's failure channel */
        Channel_ptr ltt_fail_chan;      /* Ltt's failure channel */
 
-#if defined (HOST) || defined (INKERNEL)
        /* RUP structure for HOST to driver communications */
        struct RUP rup;
-#endif
        struct RUP link_rup;    /* RUP for the link (POLL,
                                   topology etc.) */
        WORD attached_link;     /* Number of attached link */
index 36aad4c..79b8531 100644 (file)
@@ -44,8 +44,6 @@ static char *_rio_list_h_sccs = "@(#)list.h   1.9";
 
 #define PKT_IN_USE    0x1
 
-#ifdef INKERNEL
-
 #define ZERO_PTR (ushort) 0x8000
 #define        CaD     PortP->Caddr
 
@@ -54,143 +52,5 @@ static char *_rio_list_h_sccs = "@(#)list.h 1.9";
 ** to by the TxAdd pointer has PKT_IN_USE clear in its address.
 */
 
-#ifndef linux
-#if defined( MIPS ) && !defined( MIPSEISA )
-/* May the shoes of the Devil dance on your grave for creating this */
-#define   can_add_transmit(PacketP,PortP) \
-          (!((uint)(PacketP = (struct PKT *)RIO_PTR(CaD,RINDW(PortP->TxAdd))) \
-          & (PKT_IN_USE<<2)))
-
-#elif  defined(MIPSEISA) || defined(nx6000) || \
-       defined(drs6000)  || defined(UWsparc)
-
-#define   can_add_transmit(PacketP,PortP) \
-          (!((uint)(PacketP = (struct PKT *)RIO_PTR(CaD,RINDW(PortP->TxAdd))) \
-         & PKT_IN_USE))
-
-#else
-#define   can_add_transmit(PacketP,PortP) \
-          (!((uint)(PacketP = (struct PKT *)RIO_PTR(CaD,*PortP->TxAdd)) \
-         & PKT_IN_USE))
-#endif
-
-/*
-** To add a packet to the queue, you set the PKT_IN_USE bit in the address,
-** and then move the TxAdd pointer along one position to point to the next
-** packet pointer. You must wrap the pointer from the end back to the start.
-*/
-#if defined(MIPS) || defined(nx6000) || defined(drs6000) || defined(UWsparc)
-#   define add_transmit(PortP)  \
-       WINDW(PortP->TxAdd,RINDW(PortP->TxAdd) | PKT_IN_USE);\
-       if (PortP->TxAdd == PortP->TxEnd)\
-           PortP->TxAdd = PortP->TxStart;\
-       else\
-           PortP->TxAdd++;\
-       WWORD(PortP->PhbP->tx_add , RIO_OFF(CaD,PortP->TxAdd));
-#elif defined(AIX)
-#   define add_transmit(PortP)  \
-       {\
-           register ushort *TxAddP = (ushort *)RIO_PTR(Cad,PortP->TxAddO);\
-           WINDW( TxAddP, RINDW( TxAddP ) | PKT_IN_USE );\
-           if (PortP->TxAddO == PortP->TxEndO )\
-               PortP->TxAddO = PortP->TxStartO;\
-           else\
-               PortP->TxAddO += sizeof(ushort);\
-           WWORD(((PHB *)RIO_PTR(Cad,PortP->PhbO))->tx_add , PortP->TxAddO );\
-       }
-#else
-#   define add_transmit(PortP)  \
-       *PortP->TxAdd |= PKT_IN_USE;\
-       if (PortP->TxAdd == PortP->TxEnd)\
-           PortP->TxAdd = PortP->TxStart;\
-       else\
-           PortP->TxAdd++;\
-       PortP->PhbP->tx_add = RIO_OFF(CaD,PortP->TxAdd);
-#endif
-
-/*
-** can_remove_receive( PacketP, PortP ) returns non-zero if PKT_IN_USE is set
-** for the next packet on the queue. It will also set PacketP to point to the
-** relevant packet, [having cleared the PKT_IN_USE bit]. If PKT_IN_USE is clear,
-** then can_remove_receive() returns 0.
-*/
-#if defined(MIPS) || defined(nx6000) || defined(drs6000) || defined(UWsparc)
-#   define can_remove_receive(PacketP,PortP) \
-       ((RINDW(PortP->RxRemove) & PKT_IN_USE) ? \
-       (PacketP=(struct PKT *)RIO_PTR(CaD,(RINDW(PortP->RxRemove) & ~PKT_IN_USE))):0)
-#elif defined(AIX)
-#   define can_remove_receive(PacketP,PortP) \
-       ((RINDW((ushort *)RIO_PTR(Cad,PortP->RxRemoveO)) & PKT_IN_USE) ? \
-       (PacketP=(struct PKT *)RIO_PTR(Cad,RINDW((ushort *)RIO_PTR(Cad,PortP->RxRemoveO)) & ~PKT_IN_USE)):0)
-#else
-#   define can_remove_receive(PacketP,PortP) \
-       ((*PortP->RxRemove & PKT_IN_USE) ? \
-       (PacketP=(struct PKT *)RIO_PTR(CaD,(*PortP->RxRemove & ~PKT_IN_USE))):0)
-#endif
-
-
-/*
-** Will God see it within his heart to forgive us for this thing that
-** we have created? To remove a packet from the receive queue you clear
-** its PKT_IN_USE bit, and then bump the pointers. Once the pointers
-** get to the end, they must be wrapped back to the start.
-*/
-#if defined(MIPS) || defined(nx6000) || defined(drs6000) || defined(UWsparc)
-#   define remove_receive(PortP) \
-       WINDW(PortP->RxRemove, (RINDW(PortP->RxRemove) & ~PKT_IN_USE));\
-       if (PortP->RxRemove == PortP->RxEnd)\
-           PortP->RxRemove = PortP->RxStart;\
-       else\
-           PortP->RxRemove++;\
-       WWORD(PortP->PhbP->rx_remove , RIO_OFF(CaD,PortP->RxRemove));
-#elif defined(AIX)
-#   define remove_receive(PortP) \
-    {\
-        register ushort *RxRemoveP = (ushort *)RIO_PTR(Cad,PortP->RxRemoveO);\
-        WINDW( RxRemoveP, RINDW( RxRemoveP ) & ~PKT_IN_USE );\
-        if (PortP->RxRemoveO == PortP->RxEndO)\
-            PortP->RxRemoveO = PortP->RxStartO;\
-        else\
-            PortP->RxRemoveO += sizeof(ushort);\
-        WWORD(((PHB *)RIO_PTR(Cad,PortP->PhbO))->rx_remove, PortP->RxRemoveO );\
-    }
-#else
-#   define remove_receive(PortP) \
-       *PortP->RxRemove &= ~PKT_IN_USE;\
-       if (PortP->RxRemove == PortP->RxEnd)\
-           PortP->RxRemove = PortP->RxStart;\
-       else\
-           PortP->RxRemove++;\
-       PortP->PhbP->rx_remove = RIO_OFF(CaD,PortP->RxRemove);
-#endif
-#endif
-
-
-#else                          /* !IN_KERNEL */
-
-#define ZERO_PTR NULL
-
-
-#ifdef HOST
-/* #define can_remove_transmit(pkt,phb) ((((char*)pkt = (*(char**)(phb->tx_remove))-1) || 1)) && (*phb->u3.s2.tx_remove_ptr & PKT_IN_USE))   */
-#define remove_transmit(phb) *phb->u3.s2.tx_remove_ptr &= ~(ushort)PKT_IN_USE;\
-                             if (phb->tx_remove == phb->tx_end)\
-                                phb->tx_remove = phb->tx_start;\
-                             else\
-                                phb->tx_remove++;
-#define can_add_receive(phb) !(*phb->u4.s2.rx_add_ptr & PKT_IN_USE)
-#define add_receive(pkt,phb) *phb->rx_add = pkt;\
-                             *phb->u4.s2.rx_add_ptr |= PKT_IN_USE;\
-                             if (phb->rx_add == phb->rx_end)\
-                                phb->rx_add = phb->rx_start;\
-                             else\
-                                phb->rx_add++;
-#endif
-#endif
-
-#ifdef RTA
-#define splx(oldspl)    if ((oldspl) == 0) spl0()
-#endif
-
 #endif                         /* ifndef _list.h */
 /*********** end of file ***********/
index fe4e005..e24acc1 100644 (file)
@@ -78,14 +78,9 @@ struct PARM_MAP {
        WORD idle_count;        /* Idle time counter */
        WORD busy_count;        /* Busy counter */
        WORD idle_control;      /* Control Idle Process */
-#if defined(HOST) || defined(INKERNEL)
        WORD tx_intr;           /* TX interrupt pending */
        WORD rx_intr;           /* RX interrupt pending */
        WORD rup_intr;          /* RUP interrupt pending */
-#endif
-#if defined(RTA)
-       WORD dying_count;       /* Count of processes dead */
-#endif
 };
 
 #endif
index 3baebf8..2663ca0 100644 (file)
 #endif
 
 
- /*************************************************
-  * Set the LIMIT values.
-  ************************************************/
-#ifdef RTA
-#define RX_LIMIT       (ushort) 3
-#endif
-#ifdef HOST
-#define RX_LIMIT       (ushort) 1
-#endif
-
-
 /*************************************************
  * Handshake asserted. Deasserted by the LTT(s)
  ************************************************/
 /*************************************************
  * Maximum number of PHB's
  ************************************************/
-#if defined (HOST) || defined (INKERNEL)
 #define MAX_PHB               ((ushort) 128)   /* range 0-127 */
-#else
-#define MAX_PHB               ((ushort) 8)     /* range 0-7 */
-#endif
 
 /*************************************************
  * Defines for the mode fields
  * the start. The pointer tx_add points to a SPACE to put a Packet.
  * The pointer tx_remove points to the next Packet to remove
  *************************************************************************/
-#ifndef INKERNEL
-#define src_unit     u2.s2.unit
-#define src_port     u2.s2.port
-#define dest_unit    u1.s1.unit
-#define dest_port    u1.s1.port
-#endif
-#ifdef HOST
-#define tx_start     u3.s1.tx_start_ptr_ptr
-#define tx_add       u3.s1.tx_add_ptr_ptr
-#define tx_end       u3.s1.tx_end_ptr_ptr
-#define tx_remove    u3.s1.tx_remove_ptr_ptr
-#define rx_start     u4.s1.rx_start_ptr_ptr
-#define rx_add       u4.s1.rx_add_ptr_ptr
-#define rx_end       u4.s1.rx_end_ptr_ptr
-#define rx_remove    u4.s1.rx_remove_ptr_ptr
-#endif
 typedef struct PHB PHB;
 struct PHB {
-#ifdef RTA
-       ushort port;
-#endif
-#ifdef INKERNEL
        WORD source;
-#else
-       union {
-               ushort source;  /* Complete source */
-               struct {
-                       unsigned char unit;     /* Source unit */
-                       unsigned char port;     /* Source port */
-               } s2;
-       } u2;
-#endif
        WORD handshake;
        WORD status;
        NUMBER timeout;         /* Maximum of 1.9 seconds */
        WORD link;              /* Send down this link */
-#ifdef INKERNEL
        WORD destination;
-#else
-       union {
-               ushort destination;     /* Complete destination */
-               struct {
-                       unsigned char unit;     /* Destination unit */
-                       unsigned char port;     /* Destination port */
-               } s1;
-       } u1;
-#endif
-#ifdef RTA
-       ushort tx_pkts_added;
-       ushort tx_pkts_removed;
-       Q_BUF_ptr tx_q_start;   /* Start of the Q list chain */
-       short num_tx_q_bufs;    /* Number of Q buffers in the chain */
-       PKT_ptr_ptr tx_add;     /* Add a new Packet here */
-       Q_BUF_ptr tx_add_qb;    /* Pointer to the add Q buf */
-       PKT_ptr_ptr tx_add_st_qbb;      /* Pointer to start of the Q's buf */
-       PKT_ptr_ptr tx_add_end_qbb;     /* Pointer to the end of the Q's buf */
-       PKT_ptr_ptr tx_remove;  /* Remove a Packet here */
-       Q_BUF_ptr tx_remove_qb; /* Pointer to the remove Q buf */
-       PKT_ptr_ptr tx_remove_st_qbb;   /* Pointer to the start of the Q buf */
-       PKT_ptr_ptr tx_remove_end_qbb;  /* Pointer to the end of the Q buf */
-#endif
-#ifdef INKERNEL
        PKT_ptr_ptr tx_start;
        PKT_ptr_ptr tx_end;
        PKT_ptr_ptr tx_add;
        PKT_ptr_ptr tx_remove;
-#endif
-#ifdef HOST
-       union {
-               struct {
-                       PKT_ptr_ptr tx_start_ptr_ptr;
-                       PKT_ptr_ptr tx_end_ptr_ptr;
-                       PKT_ptr_ptr tx_add_ptr_ptr;
-                       PKT_ptr_ptr tx_remove_ptr_ptr;
-               } s1;
-               struct {
-                       ushort *tx_start_ptr;
-                       ushort *tx_end_ptr;
-                       ushort *tx_add_ptr;
-                       ushort *tx_remove_ptr;
-               } s2;
-       } u3;
-#endif
 
-#ifdef  RTA
-       ushort rx_pkts_added;
-       ushort rx_pkts_removed;
-       Q_BUF_ptr rx_q_start;   /* Start of the Q list chain */
-       short num_rx_q_bufs;    /* Number of Q buffers in the chain */
-       PKT_ptr_ptr rx_add;     /* Add a new Packet here */
-       Q_BUF_ptr rx_add_qb;    /* Pointer to the add Q buf */
-       PKT_ptr_ptr rx_add_st_qbb;      /* Pointer to start of the Q's buf */
-       PKT_ptr_ptr rx_add_end_qbb;     /* Pointer to the end of the Q's buf */
-       PKT_ptr_ptr rx_remove;  /* Remove a Packet here */
-       Q_BUF_ptr rx_remove_qb; /* Pointer to the remove Q buf */
-       PKT_ptr_ptr rx_remove_st_qbb;   /* Pointer to the start of the Q buf */
-       PKT_ptr_ptr rx_remove_end_qbb;  /* Pointer to the end of the Q buf */
-#endif
-#ifdef INKERNEL
        PKT_ptr_ptr rx_start;
        PKT_ptr_ptr rx_end;
        PKT_ptr_ptr rx_add;
        PKT_ptr_ptr rx_remove;
-#endif
-#ifdef HOST
-       union {
-               struct {
-                       PKT_ptr_ptr rx_start_ptr_ptr;
-                       PKT_ptr_ptr rx_end_ptr_ptr;
-                       PKT_ptr_ptr rx_add_ptr_ptr;
-                       PKT_ptr_ptr rx_remove_ptr_ptr;
-               } s1;
-               struct {
-                       ushort *rx_start_ptr;
-                       ushort *rx_end_ptr;
-                       ushort *rx_add_ptr;
-                       ushort *rx_remove_ptr;
-               } s2;
-       } u4;
-#endif
-
-#ifdef RTA                     /* some fields for the remotes */
-       ushort flush_count;     /* Count of write flushes */
-       ushort txmode;          /* Modes for tx */
-       ushort rxmode;          /* Modes for rx */
-       ushort portmode;        /* Generic modes */
-       ushort column;          /* TAB3 column count */
-       ushort tx_subscript;    /* (TX) Subscript into data field */
-       ushort rx_subscript;    /* (RX) Subscript into data field */
-       PKT_ptr rx_incomplete;  /* Hold an incomplete packet here */
-       ushort modem_bits;      /* Modem bits to mask */
-       ushort lastModem;       /* Modem control lines. */
-       ushort addr;            /* Address for sub commands */
-       ushort MonitorTstate;   /* TRUE if monitoring tstop */
-#endif
 
 };
 
index 882fd42..7011e52 100644 (file)
 #define CONTROL_DATA_WNDW  (DATA_WNDW << 8)
 
 struct PKT {
-#ifdef INKERNEL
        BYTE dest_unit;         /* Destination Unit Id */
        BYTE dest_port;         /* Destination POrt */
        BYTE src_unit;          /* Source Unit Id */
        BYTE src_port;          /* Source POrt */
-#else
-       union {
-               ushort destination;     /* Complete destination */
-               struct {
-                       unsigned char unit;     /* Destination unit */
-                       unsigned char port;     /* Destination port */
-               } s1;
-       } u1;
-       union {
-               ushort source;  /* Complete source */
-               struct {
-                       unsigned char unit;     /* Source unit */
-                       unsigned char port;     /* Source port */
-               } s2;
-       } u2;
-#endif
-#ifdef INKERNEL
        BYTE len;
        BYTE control;
-#else
-       union {
-               ushort control;
-               struct {
-                       unsigned char len;
-                       unsigned char control;
-               } s3;
-       } u3;
-#endif
        BYTE data[PKT_MAX_DATA_LEN];
        /* Actual data :-) */
        WORD csum;              /* C-SUM */
index acd9e8e..391ffc3 100644 (file)
@@ -46,11 +46,7 @@ static char *_rio_qbuf_h_sccs = "@(#)qbuf.h  1.1";
 
 
 
-#ifdef HOST
-#define PKTS_PER_BUFFER    1
-#else
 #define PKTS_PER_BUFFER    (220 / PKT_LENGTH)
-#endif
 
 typedef struct Q_BUF Q_BUF;
 struct Q_BUF {
index 9b67e24..46084d5 100644 (file)
@@ -43,9 +43,6 @@
 #endif
 #endif
 
-#ifdef INKERNEL
-
-#if !defined(MIPSAT)
 typedef unsigned short NUMBER_ptr;
 typedef unsigned short WORD_ptr;
 typedef unsigned short BYTE_ptr;
@@ -65,69 +62,6 @@ typedef unsigned short RUP_ptr;
 typedef unsigned short short_ptr;
 typedef unsigned short u_short_ptr;
 typedef unsigned short ushort_ptr;
-#else
-/* MIPSAT types */
-typedef char RIO_POINTER[8];
-typedef RIO_POINTER NUMBER_ptr;
-typedef RIO_POINTER WORD_ptr;
-typedef RIO_POINTER BYTE_ptr;
-typedef RIO_POINTER char_ptr;
-typedef RIO_POINTER Channel_ptr;
-typedef RIO_POINTER FREE_LIST_ptr_ptr;
-typedef RIO_POINTER FREE_LIST_ptr;
-typedef RIO_POINTER LPB_ptr;
-typedef RIO_POINTER Process_ptr;
-typedef RIO_POINTER PHB_ptr;
-typedef RIO_POINTER PKT_ptr;
-typedef RIO_POINTER PKT_ptr_ptr;
-typedef RIO_POINTER Q_BUF_ptr;
-typedef RIO_POINTER Q_BUF_ptr_ptr;
-typedef RIO_POINTER ROUTE_STR_ptr;
-typedef RIO_POINTER RUP_ptr;
-typedef RIO_POINTER short_ptr;
-typedef RIO_POINTER u_short_ptr;
-typedef RIO_POINTER ushort_ptr;
-#endif
-
-#else                          /* not INKERNEL */
-typedef unsigned char BYTE;
-typedef unsigned short WORD;
-typedef unsigned long DWORD;
-typedef short NUMBER;
-typedef short *NUMBER_ptr;
-typedef unsigned short *WORD_ptr;
-typedef unsigned char *BYTE_ptr;
-typedef unsigned char uchar;
-typedef unsigned short ushort;
-typedef unsigned int uint;
-typedef unsigned long ulong;
-typedef unsigned char u_char;
-typedef unsigned short u_short;
-typedef unsigned int u_int;
-typedef unsigned long u_long;
-typedef unsigned short ERROR;
-typedef unsigned long ID;
-typedef char *char_ptr;
-typedef Channel *Channel_ptr;
-typedef struct FREE_LIST *FREE_LIST_ptr;
-typedef struct FREE_LIST **FREE_LIST_ptr_ptr;
-typedef struct LPB *LPB_ptr;
-typedef struct Process *Process_ptr;
-typedef struct PHB *PHB_ptr;
-typedef struct PKT *PKT_ptr;
-typedef struct PKT **PKT_ptr_ptr;
-typedef struct Q_BUF *Q_BUF_ptr;
-typedef struct Q_BUF **Q_BUF_ptr_ptr;
-typedef struct ROUTE_STR *ROUTE_STR_ptr;
-typedef struct RUP *RUP_ptr;
-typedef short *short_ptr;
-typedef u_short *u_short_ptr;
-typedef ushort *ushort_ptr;
-typedef struct PKT PKT;
-typedef struct LPB LPB;
-typedef struct RUP RUP;
-#endif
-
 
 #endif                         /* __riotypes__ */
 
index 8d44fec..f74f67c 100644 (file)
 #endif
 #endif
 
-#if defined( HOST ) || defined( INKERNEL )
 #define MAX_RUP          ((short) 16)
-#endif
-#ifdef RTA
-#define MAX_RUP          ((short) 1)
-#endif
 
 #define PKTS_PER_RUP     ((short) 2)   /* They are always used in pairs */
 
index 3149405..6f754e1 100644 (file)
 #endif
 
 
-#if !defined( HOST ) && !defined( INKERNEL )
-#define RTA 1
-#endif
-
 #define NUM_FREE_LIST_UNITS     500
 
 #ifndef FALSE
index 0949dce..7edc6a4 100644 (file)
@@ -433,7 +433,7 @@ static void rp_do_receive(struct r_port *info,
                count += ToRecv;
        }
        /*  Push the data up to the tty layer */
-       ld->receive_buf(tty, cbuf, fbuf, count);
+       ld->receive_buf(tty, chead, fhead, count);
 done:
        tty_ldisc_deref(ld);
 }
index 64bf89c..c2490e2 100644 (file)
@@ -931,7 +931,7 @@ static int sx_set_real_termios (void *ptr)
        case CS6:sx_write_channel_byte (port, hi_mask, 0x3f);break;
        case CS5:sx_write_channel_byte (port, hi_mask, 0x1f);break;
        default:
-               printk (KERN_INFO "sx: Invalid wordsize: %d\n", CFLAG & CSIZE);
+               printk (KERN_INFO "sx: Invalid wordsize: %u\n", CFLAG & CSIZE);
                break;
        }
 
@@ -958,7 +958,7 @@ static int sx_set_real_termios (void *ptr)
        } else {
                set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags);
        }
-       sx_dprintk (SX_DEBUG_TERMIOS, "iflags: %x(%d) ", 
+       sx_dprintk (SX_DEBUG_TERMIOS, "iflags: %x(%d) ",
                    port->gs.tty->termios->c_iflag, 
                    I_OTHER(port->gs.tty));
 
@@ -973,7 +973,7 @@ static int sx_set_real_termios (void *ptr)
        } else {
                clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags);
        }
-       sx_dprintk (SX_DEBUG_TERMIOS, "oflags: %x(%d)\n", 
+       sx_dprintk (SX_DEBUG_TERMIOS, "oflags: %x(%d)\n",
                    port->gs.tty->termios->c_oflag, 
                    O_OTHER(port->gs.tty));
        /* port->c_dcd = sx_get_CD (port); */
index eb8b5be..076e07c 100644 (file)
@@ -253,6 +253,7 @@ static void tty_buffer_free_all(struct tty_struct *tty)
 
 static void tty_buffer_init(struct tty_struct *tty)
 {
+       spin_lock_init(&tty->buf.lock);
        tty->buf.head = NULL;
        tty->buf.tail = NULL;
        tty->buf.free = NULL;
@@ -266,6 +267,7 @@ static struct tty_buffer *tty_buffer_alloc(size_t size)
        p->used = 0;
        p->size = size;
        p->next = NULL;
+       p->active = 0;
        p->char_buf_ptr = (char *)(p->data);
        p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
 /*     printk("Flip create %p\n", p); */
@@ -312,25 +314,36 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
 
 int tty_buffer_request_room(struct tty_struct *tty, size_t size)
 {
-       struct tty_buffer *b = tty->buf.tail, *n;
-       int left = 0;
+       struct tty_buffer *b, *n;
+       int left;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->buf.lock, flags);
 
        /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
           remove this conditional if its worth it. This would be invisible
           to the callers */
-       if(b != NULL)
+       if ((b = tty->buf.tail) != NULL) {
                left = b->size - b->used;
-       if(left >= size)
-               return size;
-       /* This is the slow path - looking for new buffers to use */
-       n = tty_buffer_find(tty, size);
-       if(n == NULL)
-               return left;
-       if(b != NULL)
-               b->next = n;
-       else
-               tty->buf.head = n;
-       tty->buf.tail = n;
+               b->active = 1;
+       } else
+               left = 0;
+
+       if (left < size) {
+               /* This is the slow path - looking for new buffers to use */
+               if ((n = tty_buffer_find(tty, size)) != NULL) {
+                       if (b != NULL) {
+                               b->next = n;
+                               b->active = 0;
+                       } else
+                               tty->buf.head = n;
+                       tty->buf.tail = n;
+                       n->active = 1;
+               } else
+                       size = left;
+       }
+
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
        return size;
 }
 
@@ -396,10 +409,12 @@ EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags);
 int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size)
 {
        int space = tty_buffer_request_room(tty, size);
-       struct tty_buffer *tb = tty->buf.tail;
-       *chars = tb->char_buf_ptr + tb->used;
-       memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
-       tb->used += space;
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
+               tb->used += space;
+       }
        return space;
 }
 
@@ -416,10 +431,12 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
 int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size)
 {
        int space = tty_buffer_request_room(tty, size);
-       struct tty_buffer *tb = tty->buf.tail;
-       *chars = tb->char_buf_ptr + tb->used;
-       *flags = tb->flag_buf_ptr + tb->used;
-       tb->used += space;
+       if (likely(space)) {
+               struct tty_buffer *tb = tty->buf.tail;
+               *chars = tb->char_buf_ptr + tb->used;
+               *flags = tb->flag_buf_ptr + tb->used;
+               tb->used += space;
+       }
        return space;
 }
 
@@ -2747,20 +2764,20 @@ static void flush_to_ldisc(void *private_)
                schedule_delayed_work(&tty->buf.work, 1);
                goto out;
        }
-       spin_lock_irqsave(&tty->read_lock, flags);
-       while((tbuf = tty->buf.head) != NULL) {
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       while((tbuf = tty->buf.head) != NULL && !tbuf->active) {
                tty->buf.head = tbuf->next;
                if (tty->buf.head == NULL)
                        tty->buf.tail = NULL;
-               spin_unlock_irqrestore(&tty->read_lock, flags);
+               spin_unlock_irqrestore(&tty->buf.lock, flags);
                /* printk("Process buffer %p for %d\n", tbuf, tbuf->used); */
                disc->receive_buf(tty, tbuf->char_buf_ptr,
                                       tbuf->flag_buf_ptr,
                                       tbuf->used);
-               spin_lock_irqsave(&tty->read_lock, flags);
+               spin_lock_irqsave(&tty->buf.lock, flags);
                tty_buffer_free(tty, tbuf);
        }
-       spin_unlock_irqrestore(&tty->read_lock, flags);
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
 out:
        tty_ldisc_deref(disc);
 }
@@ -2852,6 +2869,12 @@ EXPORT_SYMBOL(tty_get_baud_rate);
 
 void tty_flip_buffer_push(struct tty_struct *tty)
 {
+       unsigned long flags;
+       spin_lock_irqsave(&tty->buf.lock, flags);
+       if (tty->buf.tail != NULL)
+               tty->buf.tail->active = 0;
+       spin_unlock_irqrestore(&tty->buf.lock, flags);
+
        if (tty->low_latency)
                flush_to_ldisc((void *) tty);
        else
index 9517646..7a4dfb9 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
+#include <linux/ioport.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
@@ -181,11 +182,14 @@ static int __init watchdog_init(void)
 {
        int ret;
 
+       if (!request_region(EPXC3_WATCHDOG_CTL_REG, 2, "epxc3_watchdog"))
+               return -EBUSY;
+
        ret = register_reboot_notifier(&epx_c3_notifier);
        if (ret) {
                printk(KERN_ERR PFX "cannot register reboot notifier "
                        "(err=%d)\n", ret);
-               return ret;
+               goto out;
        }
 
        ret = misc_register(&epx_c3_miscdev);
@@ -193,18 +197,23 @@ static int __init watchdog_init(void)
                printk(KERN_ERR PFX "cannot register miscdev on minor=%d "
                        "(err=%d)\n", WATCHDOG_MINOR, ret);
                unregister_reboot_notifier(&epx_c3_notifier);
-               return ret;
+               goto out;
        }
 
        printk(banner);
 
        return 0;
+
+out:
+       release_region(EPXC3_WATCHDOG_CTL_REG, 2);
+       return ret;
 }
 
 static void __exit watchdog_exit(void)
 {
        misc_deregister(&epx_c3_miscdev);
        unregister_reboot_notifier(&epx_c3_notifier);
+       release_region(EPXC3_WATCHDOG_CTL_REG, 2);
 }
 
 module_init(watchdog_init);
index 4819e7f..d94331c 100644 (file)
@@ -46,7 +46,7 @@ config EDAC_MM_EDAC
 
 config EDAC_AMD76X
        tristate "AMD 76x (760, 762, 768)"
-       depends on EDAC_MM_EDAC  && PCI
+       depends on EDAC_MM_EDAC && PCI && X86_32
        help
          Support for error detection and correction on the AMD 76x
          series of chipsets used with the Athlon processor.
index 770a5a6..c454ded 100644 (file)
@@ -1039,10 +1039,10 @@ MODULE_DEVICE_TABLE(pci, e752x_pci_tbl);
 
 
 static struct pci_driver e752x_driver = {
-      name: BS_MOD_STR,
-      probe: e752x_init_one,
-      remove: __devexit_p(e752x_remove_one),
-      id_table: e752x_pci_tbl,
+       .name = BS_MOD_STR,
+       .probe = e752x_init_one,
+       .remove = __devexit_p(e752x_remove_one),
+       .id_table = e752x_pci_tbl,
 };
 
 
index 4be9bd0..b10ee46 100644 (file)
@@ -14,7 +14,6 @@
 
 
 #include <linux/config.h>
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
 #include <linux/kernel.h>
index 1c81174..d633081 100644 (file)
@@ -52,9 +52,9 @@ config IDE
 
 if IDE
 
-config IDE_MAX_HWIFS 
+config IDE_MAX_HWIFS
        int "Max IDE interfaces"
-       depends on ALPHA || SUPERH
+       depends on ALPHA || SUPERH || IA64
        default 4
        help
          This is the maximum number of IDE hardware interfaces that will
@@ -162,8 +162,8 @@ config BLK_DEV_IDECS
        tristate "PCMCIA IDE support"
        depends on PCMCIA
        help
-         Support for outboard IDE disks, tape drives, and CD-ROM drives
-         connected through a  PCMCIA card.
+         Support for Compact Flash cards, outboard IDE disks, tape drives,
+         and CD-ROM drives connected through a PCMCIA card.
 
 config BLK_DEV_IDECD
        tristate "Include IDE/ATAPI CDROM support"
@@ -267,7 +267,7 @@ config IDE_TASK_IOCTL
        help
          This is a direct raw access to the media.  It is a complex but
          elegant solution to test and validate the domain of the hardware and
-         perform below the driver data recover if needed.  This is the most
+         perform below the driver data recovery if needed.  This is the most
          basic form of media-forensics.
 
          If you are unsure, say N here.
@@ -525,7 +525,7 @@ config BLK_DEV_CS5520
        tristate "Cyrix CS5510/20 MediaGX chipset support (VERY EXPERIMENTAL)"
        depends on EXPERIMENTAL
        help
-         Include support for PIO tuning an virtual DMA on the Cyrix MediaGX 
+         Include support for PIO tuning and virtual DMA on the Cyrix MediaGX
          5510/5520 chipset. This will automatically be detected and
          configured if found.
 
@@ -662,7 +662,7 @@ config PDC202XX_BURST
 
          It was originally designed for the PDC20246/Ultra33, whose BIOS will
          only setup UDMA on the first two PDC20246 cards.  It has also been
-         used succesfully on a PDC20265/Ultra100, allowing use of UDMA modes
+         used successfully on a PDC20265/Ultra100, allowing use of UDMA modes
          when the PDC20265 BIOS has been disabled (for faster boot up).
 
          Please read the comments at the top of
@@ -673,13 +673,6 @@ config PDC202XX_BURST
 config BLK_DEV_PDC202XX_NEW
        tristate "PROMISE PDC202{68|69|70|71|75|76|77} support"
 
-# FIXME - probably wants to be one for old and for new
-config PDC202XX_FORCE
-       bool "Enable controller even if disabled by BIOS"
-       depends on BLK_DEV_PDC202XX_NEW
-       help
-         Enable the PDC202xx controller even if it has been disabled in the BIOS setup.
-
 config BLK_DEV_SVWKS
        tristate "ServerWorks OSB4/CSB5/CSB6 chipsets support"
        help
@@ -722,7 +715,7 @@ config BLK_DEV_SIS5513
 config BLK_DEV_SLC90E66
        tristate "SLC90E66 chipset support"
        help
-         This driver ensures (U)DMA support for Victroy66 SouthBridges for
+         This driver ensures (U)DMA support for Victory66 SouthBridges for
          SMsC with Intel NorthBridges.  This is an Ultra66 based chipset.
          The nice thing about it is that you can mix Ultra/DMA/PIO devices
          and it will handle timing cycles.  Since this is an improved
@@ -1060,7 +1053,7 @@ config IDEDMA_IVB
          in that mode with an 80c ribbon.
 
          If you are experiencing compatibility or performance problems, you
-         MAY try to answering Y here. However, it does not necessarily solve
+         MAY try to answer Y here. However, it does not necessarily solve
          any of your problems, it could even cause more of them.
 
          It is normally safe to answer Y; however, the default is N.
index ca25f9e..6c60a9d 100644 (file)
@@ -776,7 +776,7 @@ static void update_ordered(ide_drive_t *drive)
                         ide_id_has_flush_cache_ext(id));
 
                printk(KERN_INFO "%s: cache flushes %ssupported\n",
-                      drive->name, barrier ? "" : "not");
+                      drive->name, barrier ? "" : "not ");
 
                if (barrier) {
                        ordered = QUEUE_ORDERED_DRAIN_FLUSH;
@@ -889,11 +889,7 @@ static void idedisk_setup (ide_drive_t *drive)
        if (drive->id_read == 0)
                return;
 
-       /*
-        * CompactFlash cards and their brethern look just like hard drives
-        * to us, but they are removable and don't have a doorlock mechanism.
-        */
-       if (drive->removable && !(drive->is_flash)) {
+       if (drive->removable) {
                /*
                 * Removable disks (eg. SYQUEST); ignore 'WD' drives 
                 */
index 8d50df4..c01615d 100644 (file)
@@ -55,8 +55,8 @@
 #include <asm/io.h>
 #include <asm/bitops.h>
 
-int __ide_end_request(ide_drive_t *drive, struct request *rq, int uptodate,
-                     int nr_sectors)
+static int __ide_end_request(ide_drive_t *drive, struct request *rq,
+                            int uptodate, int nr_sectors)
 {
        int ret = 1;
 
@@ -91,7 +91,6 @@ int __ide_end_request(ide_drive_t *drive, struct request *rq, int uptodate,
 
        return ret;
 }
-EXPORT_SYMBOL(__ide_end_request);
 
 /**
  *     ide_end_request         -       complete an IDE I/O
index af7af95..b72dde7 100644 (file)
@@ -1243,6 +1243,7 @@ int ide_wait_not_busy(ide_hwif_t *hwif, unsigned long timeout)
                 */
                if (stat == 0xff)
                        return -ENODEV;
+               touch_softlockup_watchdog();
        }
        return -EBUSY;
 }
index e742554..427d1c2 100644 (file)
@@ -125,45 +125,6 @@ static void ide_disk_init_mult_count(ide_drive_t *drive)
 }
 
 /**
- *     drive_is_flashcard      -       check for compact flash
- *     @drive: drive to check
- *
- *     CompactFlash cards and their brethern pretend to be removable
- *     hard disks, except:
- *             (1) they never have a slave unit, and
- *             (2) they don't have doorlock mechanisms.
- *     This test catches them, and is invoked elsewhere when setting
- *     appropriate config bits.
- *
- *     FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD)
- *     devices, so in linux 2.3.x we should change this to just treat all
- *     PCMCIA  drives this way, and get rid of the model-name tests below
- *     (too big of an interface change for 2.4.x).
- *     At that time, we might also consider parameterizing the timeouts and
- *     retries, since these are MUCH faster than mechanical drives. -M.Lord
- */
-static inline int drive_is_flashcard (ide_drive_t *drive)
-{
-       struct hd_driveid *id = drive->id;
-
-       if (drive->removable) {
-               if (id->config == 0x848a) return 1;     /* CompactFlash */
-               if (!strncmp(id->model, "KODAK ATA_FLASH", 15)  /* Kodak */
-                || !strncmp(id->model, "Hitachi CV", 10)       /* Hitachi */
-                || !strncmp(id->model, "SunDisk SDCFB", 13)    /* old SanDisk */
-                || !strncmp(id->model, "SanDisk SDCFB", 13)    /* SanDisk */
-                || !strncmp(id->model, "HAGIWARA HPC", 12)     /* Hagiwara */
-                || !strncmp(id->model, "LEXAR ATA_FLASH", 15)  /* Lexar */
-                || !strncmp(id->model, "ATA_FLASH", 9))        /* Simple Tech */
-               {
-                       return 1;       /* yes, it is a flash memory card */
-               }
-       }
-       return 0;       /* no, it is not a flash memory card */
-}
-
-/**
  *     do_identify     -       identify a drive
  *     @drive: drive to identify 
  *     @cmd: command used
@@ -278,13 +239,17 @@ static inline void do_identify (ide_drive_t *drive, u8 cmd)
        /*
         * Not an ATAPI device: looks like a "regular" hard disk
         */
-       if (id->config & (1<<7))
+
+       /*
+        * 0x848a = CompactFlash device
+        * These are *not* removable in Linux definition of the term
+        */
+
+       if ((id->config != 0x848a) && (id->config & (1<<7)))
                drive->removable = 1;
 
-       if (drive_is_flashcard(drive))
-               drive->is_flash = 1;
        drive->media = ide_disk;
-       printk("%s DISK drive\n", (drive->is_flash) ? "CFA" : "ATA" );
+       printk("%s DISK drive\n", (id->config == 0x848a) ? "CFA" : "ATA" );
        QUIRK_LIST(drive);
        return;
 
index afeb02b..b2cc437 100644 (file)
@@ -242,7 +242,6 @@ static void init_hwif_data(ide_hwif_t *hwif, unsigned int index)
                drive->name[2]                  = 'a' + (index * MAX_DRIVES) + unit;
                drive->max_failures             = IDE_DEFAULT_MAX_FAILURES;
                drive->using_dma                = 0;
-               drive->is_flash                 = 0;
                drive->vdma                     = 0;
                INIT_LIST_HEAD(&drive->list);
                init_completion(&drive->gendev_rel_comp);
index a21b1e1..c743e68 100644 (file)
@@ -262,6 +262,21 @@ static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const ch
        else
                pci_set_drvdata(dev, (void *) aec6xxx_34_base);
 
+       /* These are necessary to get AEC6280 Macintosh cards to work */
+       if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) ||
+           (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) {
+               u8 reg49h = 0, reg4ah = 0;
+               /* Clear reset and test bits.  */
+               pci_read_config_byte(dev, 0x49, &reg49h);
+               pci_write_config_byte(dev, 0x49, reg49h & ~0x30);
+               /* Enable chip interrupt output.  */
+               pci_read_config_byte(dev, 0x4a, &reg4ah);
+               pci_write_config_byte(dev, 0x4a, reg4ah & ~0x01);
+               /* Enable burst mode. */
+               pci_read_config_byte(dev, 0x4a, &reg4ah);
+               pci_write_config_byte(dev, 0x4a, reg4ah | 0x80);
+       }
+
        return dev->irq;
 }
 
index 7b589d9..940bdd4 100644 (file)
@@ -1288,6 +1288,10 @@ static void __devinit hpt37x_clocking(ide_hwif_t *hwif)
                                goto init_hpt37X_done;
                        }
                }
+               if (!pci_get_drvdata(dev)) {
+                       printk("No Clock Stabilization!!!\n");
+                       return;
+               }
 pll_recal:
                if (adjust & 1)
                        pll -= (adjust >> 1);
index 108fda8..38f41b3 100644 (file)
@@ -733,7 +733,7 @@ static void __devinit it8212_disable_raid(struct pci_dev *dev)
 
        pci_write_config_dword(dev,0x4C, 0x02040204);
        pci_write_config_byte(dev, 0x42, 0x36);
-       pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0);
+       pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
 }
 
 static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name)
index fe06ebb..acd6317 100644 (file)
@@ -420,9 +420,6 @@ static ide_pci_device_t pdcnew_chipsets[] __devinitdata = {
                .init_hwif      = init_hwif_pdc202new,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
        },{     /* 3 */
                .name           = "PDC20271",
@@ -447,9 +444,6 @@ static ide_pci_device_t pdcnew_chipsets[] __devinitdata = {
                .init_hwif      = init_hwif_pdc202new,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
        },{     /* 6 */
                .name           = "PDC20277",
index ad9d958..6f8f864 100644 (file)
@@ -786,9 +786,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
                .init_dma       = init_dma_pdc202xx,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
                .extra          = 16,
        },{     /* 1 */
@@ -799,9 +796,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
                .init_dma       = init_dma_pdc202xx,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
                .extra          = 48,
                .flags          = IDEPCI_FLAG_FORCE_PDC,
@@ -813,9 +807,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
                .init_dma       = init_dma_pdc202xx,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
                .extra          = 48,
        },{     /* 3 */
@@ -826,9 +817,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
                .init_dma       = init_dma_pdc202xx,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
                .extra          = 48,
                .flags          = IDEPCI_FLAG_FORCE_PDC,
@@ -840,9 +828,6 @@ static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
                .init_dma       = init_dma_pdc202xx,
                .channels       = 2,
                .autodma        = AUTODMA,
-#ifndef CONFIG_PDC202XX_FORCE
-               .enablebits     = {{0x50,0x02,0x02}, {0x50,0x04,0x04}},
-#endif
                .bootable       = OFF_BOARD,
                .extra          = 48,
        }
index b3e77df..e9b83e1 100644 (file)
@@ -135,6 +135,7 @@ static u8 piix_ratemask (ide_drive_t *drive)
                case PCI_DEVICE_ID_INTEL_ICH6_19:
                case PCI_DEVICE_ID_INTEL_ICH7_21:
                case PCI_DEVICE_ID_INTEL_ESB2_18:
+               case PCI_DEVICE_ID_INTEL_ICH8_6:
                        mode = 3;
                        break;
                /* UDMA 66 capable */
@@ -449,6 +450,7 @@ static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char
                case PCI_DEVICE_ID_INTEL_ICH6_19:
                case PCI_DEVICE_ID_INTEL_ICH7_21:
                case PCI_DEVICE_ID_INTEL_ESB2_18:
+               case PCI_DEVICE_ID_INTEL_ICH8_6:
                {
                        unsigned int extra = 0;
                        pci_read_config_dword(dev, 0x54, &extra);
@@ -575,6 +577,7 @@ static ide_pci_device_t piix_pci_info[] __devinitdata = {
        /* 21 */ DECLARE_PIIX_DEV("ICH7"),
        /* 22 */ DECLARE_PIIX_DEV("ICH4"),
        /* 23 */ DECLARE_PIIX_DEV("ESB2"),
+       /* 24 */ DECLARE_PIIX_DEV("ICH8M"),
 };
 
 /**
@@ -651,6 +654,7 @@ static struct pci_device_id piix_pci_tbl[] = {
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 21},
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 22},
        { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 23},
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 24},
        { 0, },
 };
 MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
index 1b85ce1..11fe537 100644 (file)
@@ -216,7 +216,7 @@ struct Layer1 {
 #define GROUP_TEI      127
 #define TEI_SAPI       63
 #define CTRL_SAPI      0
-#define PACKET_NOACK   250
+#define PACKET_NOACK   7
 
 /* Layer2 Flags */
 
index 3314a5a..94c9afb 100644 (file)
@@ -71,14 +71,14 @@ int sc_ioctl(int card, scs_ioctl *data)
                /*
                 * Get the SRec from user space
                 */
-               if (copy_from_user(srec, data->dataptr, sizeof(srec))) {
+               if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) {
                        kfree(rcvmsg);
                        kfree(srec);
                        return -EFAULT;
                }
 
                status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
-                               0, sizeof(srec), srec, rcvmsg, SAR_TIMEOUT);
+                               0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT);
                kfree(rcvmsg);
                kfree(srec);
 
index 653d4dc..d05e312 100644 (file)
@@ -1024,7 +1024,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                rdev-> sb_size = (rdev->sb_size | bmask)+1;
 
        if (refdev == 0)
-               return 1;
+               ret = 1;
        else {
                __u64 ev1, ev2;
                struct mdp_superblock_1 *refsb = 
@@ -1044,7 +1044,9 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
                ev2 = le64_to_cpu(refsb->events);
 
                if (ev1 > ev2)
-                       return 1;
+                       ret = 1;
+               else
+                       ret = 0;
        }
        if (minor_version) 
                rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2;
@@ -1058,7 +1060,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
 
        if (le32_to_cpu(sb->size) > rdev->size*2)
                return -EINVAL;
-       return 0;
+       return ret;
 }
 
 static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
@@ -1081,7 +1083,7 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                mddev->size = le64_to_cpu(sb->size)/2;
                mddev->events = le64_to_cpu(sb->events);
                mddev->bitmap_offset = 0;
-               mddev->default_bitmap_offset = 1024;
+               mddev->default_bitmap_offset = 1024 >> 9;
                
                mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
                memcpy(mddev->uuid, sb->set_uuid, 16);
@@ -1162,7 +1164,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        sb->cnt_corrected_read = atomic_read(&rdev->corrected_errors);
 
        sb->raid_disks = cpu_to_le32(mddev->raid_disks);
-       sb->size = cpu_to_le64(mddev->size);
+       sb->size = cpu_to_le64(mddev->size<<1);
 
        if (mddev->bitmap && mddev->bitmap_file == NULL) {
                sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
@@ -2942,6 +2944,8 @@ static int get_array_info(mddev_t * mddev, void __user * arg)
        info.ctime         = mddev->ctime;
        info.level         = mddev->level;
        info.size          = mddev->size;
+       if (info.size != mddev->size) /* overflow */
+               info.size = -1;
        info.nr_disks      = nr;
        info.raid_disks    = mddev->raid_disks;
        info.md_minor      = mddev->md_minor;
@@ -3523,7 +3527,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
                )
                return -EINVAL;
        /* Check there is only one change */
-       if (mddev->size != info->size) cnt++;
+       if (info->size >= 0 && mddev->size != info->size) cnt++;
        if (mddev->raid_disks != info->raid_disks) cnt++;
        if (mddev->layout != info->layout) cnt++;
        if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) cnt++;
@@ -3540,7 +3544,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
                else
                        return mddev->pers->reconfig(mddev, info->layout, -1);
        }
-       if (mddev->size != info->size)
+       if (info->size >= 0 && mddev->size != info->size)
                rv = update_size(mddev, info->size);
 
        if (mddev->raid_disks    != info->raid_disks)
index d03f99c..678f4db 100644 (file)
@@ -372,7 +372,7 @@ out_free_conf:
        kfree(conf);
        mddev->private = NULL;
 out:
-       return 1;
+       return -ENOMEM;
 }
 
 static int raid0_stop (mddev_t *mddev)
index 9130d05..ab90a6d 100644 (file)
@@ -565,6 +565,8 @@ rb_out:
 
        if (disk >= 0 && (rdev=rcu_dereference(conf->mirrors[disk].rdev))!= NULL)
                atomic_inc(&conf->mirrors[disk].rdev->nr_pending);
+       else
+               disk = -1;
        rcu_read_unlock();
 
        return disk;
index 25976bf..2dba305 100644 (file)
@@ -350,7 +350,8 @@ static void shrink_stripes(raid5_conf_t *conf)
        while (drop_one_stripe(conf))
                ;
 
-       kmem_cache_destroy(conf->slab_cache);
+       if (conf->slab_cache)
+               kmem_cache_destroy(conf->slab_cache);
        conf->slab_cache = NULL;
 }
 
index ed2abb2..cd477eb 100644 (file)
@@ -366,7 +366,8 @@ static void shrink_stripes(raid6_conf_t *conf)
        while (drop_one_stripe(conf))
                ;
 
-       kmem_cache_destroy(conf->slab_cache);
+       if (conf->slab_cache)
+               kmem_cache_destroy(conf->slab_cache);
        conf->slab_cache = NULL;
 }
 
index 9062856..184974c 100644 (file)
@@ -60,4 +60,7 @@ extern void i2o_iop_remove(struct i2o_controller *);
 #define I2O_IN_PORT    0x40
 #define I2O_OUT_PORT   0x44
 
+/* Motorola/Freescale specific register offset */
+#define I2O_MOTOROLA_PORT_OFFSET       0x10400
+
 #define I2O_IRQ_OUTBOUND_POST  0x00000008
index d698d77..4f1515c 100644 (file)
@@ -88,6 +88,11 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c)
        struct device *dev = &pdev->dev;
        int i;
 
+       if (pci_request_regions(pdev, OSM_DESCRIPTION)) {
+               printk(KERN_ERR "%s: device already claimed\n", c->name);
+               return -ENODEV;
+       }
+
        for (i = 0; i < 6; i++) {
                /* Skip I/O spaces */
                if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
@@ -163,6 +168,24 @@ static int __devinit i2o_pci_alloc(struct i2o_controller *c)
        c->in_port = c->base.virt + I2O_IN_PORT;
        c->out_port = c->base.virt + I2O_OUT_PORT;
 
+       /* Motorola/Freescale chip does not follow spec */
+       if (pdev->vendor == PCI_VENDOR_ID_MOTOROLA && pdev->device == 0x18c0) {
+               /* Check if CPU is enabled */
+               if (be32_to_cpu(readl(c->base.virt + 0x10000)) & 0x10000000) {
+                       printk(KERN_INFO "%s: MPC82XX needs CPU running to "
+                              "service I2O.\n", c->name);
+                       i2o_pci_free(c);
+                       return -ENODEV;
+               } else {
+                       c->irq_status += I2O_MOTOROLA_PORT_OFFSET;
+                       c->irq_mask += I2O_MOTOROLA_PORT_OFFSET;
+                       c->in_port += I2O_MOTOROLA_PORT_OFFSET;
+                       c->out_port += I2O_MOTOROLA_PORT_OFFSET;
+                       printk(KERN_INFO "%s: MPC82XX workarounds activated.\n",
+                              c->name);
+               }
+       }
+
        if (i2o_dma_alloc(dev, &c->status, 8, GFP_KERNEL)) {
                i2o_pci_free(c);
                return -ENOMEM;
@@ -298,7 +321,7 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
        struct i2o_controller *c;
        int rc;
        struct pci_dev *i960 = NULL;
-       int pci_dev_busy = 0;
+       int enabled = pdev->is_enabled;
 
        printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n");
 
@@ -308,16 +331,12 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
                return -ENODEV;
        }
 
-       if ((rc = pci_enable_device(pdev))) {
-               printk(KERN_WARNING "i2o: couldn't enable device %s\n",
-                      pci_name(pdev));
-               return rc;
-       }
-
-       if (pci_request_regions(pdev, OSM_DESCRIPTION)) {
-               printk(KERN_ERR "i2o: device already claimed\n");
-               return -ENODEV;
-       }
+       if (!enabled)
+               if ((rc = pci_enable_device(pdev))) {
+                       printk(KERN_WARNING "i2o: couldn't enable device %s\n",
+                              pci_name(pdev));
+                       return rc;
+               }
 
        if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
                printk(KERN_WARNING "i2o: no suitable DMA found for %s\n",
@@ -395,9 +414,7 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
 
        if ((rc = i2o_pci_alloc(c))) {
                printk(KERN_ERR "%s: DMA / IO allocation for I2O controller "
-                      " failed\n", c->name);
-               if (rc == -ENODEV)
-                       pci_dev_busy = 1;
+                      "failed\n", c->name);
                goto free_controller;
        }
 
@@ -425,7 +442,7 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
        i2o_iop_free(c);
 
       disable:
-       if (!pci_dev_busy)
+       if (!enabled)
                pci_disable_device(pdev);
 
        return rc;
index 701620b..8b3784e 100644 (file)
@@ -110,8 +110,9 @@ static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const voi
 {
        while (len > 0) {
                map_word d;
-               d.x[0] = *((uint32_t*)from)++;
+               d.x[0] = *((uint32_t*)from);
                dc21285_write32(map, d, to);
+               from += 4;
                to += 4;
                len -= 4;
        }
@@ -121,8 +122,9 @@ static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const voi
 {
        while (len > 0) {
                map_word d;
-               d.x[0] = *((uint16_t*)from)++;
+               d.x[0] = *((uint16_t*)from);
                dc21285_write16(map, d, to);
+               from += 2;
                to += 2;
                len -= 2;
        }
@@ -131,8 +133,9 @@ static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const voi
 static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 {
        map_word d;
-       d.x[0] = *((uint8_t*)from)++;
+       d.x[0] = *((uint8_t*)from);
        dc21285_write8(map, d, to);
+       from++;
        to++;
        len--;
 }
index 7488ee7..7f47124 100644 (file)
@@ -753,9 +753,11 @@ enum tx_desc_status {
 enum ChipCaps { CapBusMaster=0x20, CapPwrMgmt=0x2000 };
 
 struct vortex_extra_stats {
-        unsigned long tx_deferred;
-        unsigned long tx_multiple_collisions;
-        unsigned long rx_bad_ssd;
+       unsigned long tx_deferred;
+       unsigned long tx_max_collisions;
+       unsigned long tx_multiple_collisions;
+       unsigned long tx_single_collisions;
+       unsigned long rx_bad_ssd;
 };
 
 struct vortex_private {
@@ -863,12 +865,14 @@ static struct {
        const char str[ETH_GSTRING_LEN];
 } ethtool_stats_keys[] = {
        { "tx_deferred" },
+       { "tx_max_collisions" },
        { "tx_multiple_collisions" },
+       { "tx_single_collisions" },
        { "rx_bad_ssd" },
 };
 
 /* number of ETHTOOL_GSTATS u64's */
-#define VORTEX_NUM_STATS     3
+#define VORTEX_NUM_STATS    5
 
 static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq,
                                   int chip_idx, int card_idx);
@@ -2108,9 +2112,12 @@ vortex_error(struct net_device *dev, int status)
                iowrite8(0, ioaddr + TxStatus);
                if (tx_status & 0x30) {                 /* txJabber or txUnderrun */
                        do_tx_reset = 1;
-               } else if ((tx_status & 0x08) && (vp->drv_flags & MAX_COLLISION_RESET)) {       /* maxCollisions */
-                       do_tx_reset = 1;
-                       reset_mask = 0x0108;            /* Reset interface logic, but not download logic */
+               } else if (tx_status & 0x08) {  /* maxCollisions */
+                       vp->xstats.tx_max_collisions++;
+                       if (vp->drv_flags & MAX_COLLISION_RESET) {
+                               do_tx_reset = 1;
+                               reset_mask = 0x0108;            /* Reset interface logic, but not download logic */
+                       }
                } else {                                                /* Merely re-enable the transmitter. */
                        iowrite16(TxEnable, ioaddr + EL3_CMD);
                }
@@ -2926,7 +2933,6 @@ static void update_stats(void __iomem *ioaddr, struct net_device *dev)
        EL3WINDOW(6);
        vp->stats.tx_carrier_errors             += ioread8(ioaddr + 0);
        vp->stats.tx_heartbeat_errors           += ioread8(ioaddr + 1);
-       vp->stats.collisions                    += ioread8(ioaddr + 3);
        vp->stats.tx_window_errors              += ioread8(ioaddr + 4);
        vp->stats.rx_fifo_errors                += ioread8(ioaddr + 5);
        vp->stats.tx_packets                    += ioread8(ioaddr + 6);
@@ -2939,10 +2945,15 @@ static void update_stats(void __iomem *ioaddr, struct net_device *dev)
        vp->stats.tx_bytes                      += ioread16(ioaddr + 12);
        /* Extra stats for get_ethtool_stats() */
        vp->xstats.tx_multiple_collisions       += ioread8(ioaddr + 2);
+       vp->xstats.tx_single_collisions         += ioread8(ioaddr + 3);
        vp->xstats.tx_deferred                  += ioread8(ioaddr + 8);
        EL3WINDOW(4);
        vp->xstats.rx_bad_ssd                   += ioread8(ioaddr + 12);
 
+       vp->stats.collisions = vp->xstats.tx_multiple_collisions
+               + vp->xstats.tx_single_collisions
+               + vp->xstats.tx_max_collisions;
+
        {
                u8 up = ioread8(ioaddr + 13);
                vp->stats.rx_bytes += (up & 0x0f) << 16;
@@ -3036,8 +3047,10 @@ static void vortex_get_ethtool_stats(struct net_device *dev,
        spin_unlock_irqrestore(&vp->lock, flags);
 
        data[0] = vp->xstats.tx_deferred;
-       data[1] = vp->xstats.tx_multiple_collisions;
-       data[2] = vp->xstats.rx_bad_ssd;
+       data[1] = vp->xstats.tx_max_collisions;
+       data[2] = vp->xstats.tx_multiple_collisions;
+       data[3] = vp->xstats.tx_single_collisions;
+       data[4] = vp->xstats.rx_bad_ssd;
 }
 
 
index f605dea..f63c387 100644 (file)
@@ -90,6 +90,15 @@ config PARPORT_ARC
        depends on ARM && PARPORT
        select PARPORT_NOT_PC
 
+config PARPORT_IP32
+       tristate "SGI IP32 builtin port (EXPERIMENTAL)"
+       depends on SGI_IP32 && PARPORT && EXPERIMENTAL
+       select PARPORT_NOT_PC
+       help
+         Say Y here if you need support for the parallel port on
+         SGI O2 machines. This code is also available as a module (say M),
+         called parport_ip32.  If in doubt, saying N is the safe plan.
+
 config PARPORT_AMIGA
        tristate "Amiga builtin port"
        depends on AMIGA && PARPORT
index 5372212..a19de35 100644 (file)
@@ -17,3 +17,4 @@ obj-$(CONFIG_PARPORT_MFC3)    += parport_mfc3.o
 obj-$(CONFIG_PARPORT_ATARI)    += parport_atari.o
 obj-$(CONFIG_PARPORT_SUNBPP)   += parport_sunbpp.o
 obj-$(CONFIG_PARPORT_GSC)      += parport_gsc.o
+obj-$(CONFIG_PARPORT_IP32)     += parport_ip32.o
index 5b887ba..690b239 100644 (file)
@@ -61,10 +61,10 @@ static void timeout_waiting_on_port (unsigned long cookie)
  *     set to zero, it returns immediately.
  *
  *     If an interrupt occurs before the timeout period elapses, this
- *     function returns one immediately.  If it times out, it returns
- *     a value greater than zero.  An error code less than zero
- *     indicates an error (most likely a pending signal), and the
- *     calling code should finish what it's doing as soon as it can.
+ *     function returns zero immediately.  If it times out, it returns
+ *     one.  An error code less than zero indicates an error (most
+ *     likely a pending signal), and the calling code should finish
+ *     what it's doing as soon as it can.
  */
 
 int parport_wait_event (struct parport *port, signed long timeout)
@@ -110,7 +110,7 @@ int parport_wait_event (struct parport *port, signed long timeout)
  *
  *     If the status lines take on the desired values before the
  *     timeout period elapses, parport_poll_peripheral() returns zero
- *     immediately.  A zero return value greater than zero indicates
+ *     immediately.  A return value greater than zero indicates
  *     a timeout.  An error code (less than zero) indicates an error,
  *     most likely a signal that arrived, and the caller should
  *     finish what it is doing as soon as possible.
diff --git a/drivers/parport/parport_ip32.c b/drivers/parport/parport_ip32.c
new file mode 100644 (file)
index 0000000..46e06e5
--- /dev/null
@@ -0,0 +1,2253 @@
+/* Low-level parallel port routines for built-in port on SGI IP32
+ *
+ * Author: Arnaud Giersch <arnaud.giersch@free.fr>
+ *
+ * Based on parport_pc.c by
+ *     Phil Blundell, Tim Waugh, Jose Renau, David Campbell,
+ *     Andrea Arcangeli, et al.
+ *
+ * Thanks to Ilya A. Volynets-Evenbakh for his help.
+ *
+ * Copyright (C) 2005, 2006 Arnaud Giersch.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Current status:
+ *
+ *     Basic SPP and PS2 modes are supported.
+ *     Support for parallel port IRQ is present.
+ *     Hardware SPP (a.k.a. compatibility), EPP, and ECP modes are
+ *     supported.
+ *     SPP/ECP FIFO can be driven in PIO or DMA mode.  PIO mode can work with
+ *     or without interrupt support.
+ *
+ *     Hardware ECP mode is not fully implemented (ecp_read_data and
+ *     ecp_write_addr are actually missing).
+ *
+ * To do:
+ *
+ *     Fully implement ECP mode.
+ *     EPP and ECP mode need to be tested.  I currently do not own any
+ *     peripheral supporting these extended mode, and cannot test them.
+ *     If DMA mode works well, decide if support for PIO FIFO modes should be
+ *     dropped.
+ *     Use the io{read,write} family functions when they become available in
+ *     the linux-mips.org tree.  Note: the MIPS specific functions readsb()
+ *     and writesb() are to be translated by ioread8_rep() and iowrite8_rep()
+ *     respectively.
+ */
+
+/* The built-in parallel port on the SGI 02 workstation (a.k.a. IP32) is an
+ * IEEE 1284 parallel port driven by a Texas Instrument TL16PIR552PH chip[1].
+ * This chip supports SPP, bidirectional, EPP and ECP modes.  It has a 16 byte
+ * FIFO buffer and supports DMA transfers.
+ *
+ * [1] http://focus.ti.com/docs/prod/folders/print/tl16pir552.html
+ *
+ * Theoretically, we could simply use the parport_pc module.  It is however
+ * not so simple.  The parport_pc code assumes that the parallel port
+ * registers are port-mapped.  On the O2, they are memory-mapped.
+ * Furthermore, each register is replicated on 256 consecutive addresses (as
+ * it is for the built-in serial ports on the same chip).
+ */
+
+/*--- Some configuration defines ---------------------------------------*/
+
+/* DEBUG_PARPORT_IP32
+ *     0       disable debug
+ *     1       standard level: pr_debug1 is enabled
+ *     2       parport_ip32_dump_state is enabled
+ *     >=3     verbose level: pr_debug is enabled
+ */
+#if !defined(DEBUG_PARPORT_IP32)
+#      define DEBUG_PARPORT_IP32  0    /* 0 (disabled) for production */
+#endif
+
+/*----------------------------------------------------------------------*/
+
+/* Setup DEBUG macros.  This is done before any includes, just in case we
+ * activate pr_debug() with DEBUG_PARPORT_IP32 >= 3.
+ */
+#if DEBUG_PARPORT_IP32 == 1
+#      warning DEBUG_PARPORT_IP32 == 1
+#elif DEBUG_PARPORT_IP32 == 2
+#      warning DEBUG_PARPORT_IP32 == 2
+#elif DEBUG_PARPORT_IP32 >= 3
+#      warning DEBUG_PARPORT_IP32 >= 3
+#      if !defined(DEBUG)
+#              define DEBUG /* enable pr_debug() in kernel.h */
+#      endif
+#endif
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <asm/io.h>
+#include <asm/ip32/ip32_ints.h>
+#include <asm/ip32/mace.h>
+
+/*--- Global variables -------------------------------------------------*/
+
+/* Verbose probing on by default for debugging. */
+#if DEBUG_PARPORT_IP32 >= 1
+#      define DEFAULT_VERBOSE_PROBING  1
+#else
+#      define DEFAULT_VERBOSE_PROBING  0
+#endif
+
+/* Default prefix for printk */
+#define PPIP32 "parport_ip32: "
+
+/*
+ * These are the module parameters:
+ * @features:          bit mask of features to enable/disable
+ *                     (all enabled by default)
+ * @verbose_probing:   log chit-chat during initialization
+ */
+#define PARPORT_IP32_ENABLE_IRQ        (1U << 0)
+#define PARPORT_IP32_ENABLE_DMA        (1U << 1)
+#define PARPORT_IP32_ENABLE_SPP        (1U << 2)
+#define PARPORT_IP32_ENABLE_EPP        (1U << 3)
+#define PARPORT_IP32_ENABLE_ECP        (1U << 4)
+static unsigned int features = ~0U;
+static int verbose_probing =   DEFAULT_VERBOSE_PROBING;
+
+/* We do not support more than one port. */
+static struct parport *this_port = NULL;
+
+/* Timing constants for FIFO modes.  */
+#define FIFO_NFAULT_TIMEOUT    100     /* milliseconds */
+#define FIFO_POLLING_INTERVAL  50      /* microseconds */
+
+/*--- I/O register definitions -----------------------------------------*/
+
+/**
+ * struct parport_ip32_regs - virtual addresses of parallel port registers
+ * @data:      Data Register
+ * @dsr:       Device Status Register
+ * @dcr:       Device Control Register
+ * @eppAddr:   EPP Address Register
+ * @eppData0:  EPP Data Register 0
+ * @eppData1:  EPP Data Register 1
+ * @eppData2:  EPP Data Register 2
+ * @eppData3:  EPP Data Register 3
+ * @ecpAFifo:  ECP Address FIFO
+ * @fifo:      General FIFO register.  The same address is used for:
+ *             - cFifo, the Parallel Port DATA FIFO
+ *             - ecpDFifo, the ECP Data FIFO
+ *             - tFifo, the ECP Test FIFO
+ * @cnfgA:     Configuration Register A
+ * @cnfgB:     Configuration Register B
+ * @ecr:       Extended Control Register
+ */
+struct parport_ip32_regs {
+       void __iomem *data;
+       void __iomem *dsr;
+       void __iomem *dcr;
+       void __iomem *eppAddr;
+       void __iomem *eppData0;
+       void __iomem *eppData1;
+       void __iomem *eppData2;
+       void __iomem *eppData3;
+       void __iomem *ecpAFifo;
+       void __iomem *fifo;
+       void __iomem *cnfgA;
+       void __iomem *cnfgB;
+       void __iomem *ecr;
+};
+
+/* Device Status Register */
+#define DSR_nBUSY              (1U << 7)       /* PARPORT_STATUS_BUSY */
+#define DSR_nACK               (1U << 6)       /* PARPORT_STATUS_ACK */
+#define DSR_PERROR             (1U << 5)       /* PARPORT_STATUS_PAPEROUT */
+#define DSR_SELECT             (1U << 4)       /* PARPORT_STATUS_SELECT */
+#define DSR_nFAULT             (1U << 3)       /* PARPORT_STATUS_ERROR */
+#define DSR_nPRINT             (1U << 2)       /* specific to TL16PIR552 */
+/* #define DSR_reserved                (1U << 1) */
+#define DSR_TIMEOUT            (1U << 0)       /* EPP timeout */
+
+/* Device Control Register */
+/* #define DCR_reserved                (1U << 7) | (1U <<  6) */
+#define DCR_DIR                        (1U << 5)       /* direction */
+#define DCR_IRQ                        (1U << 4)       /* interrupt on nAck */
+#define DCR_SELECT             (1U << 3)       /* PARPORT_CONTROL_SELECT */
+#define DCR_nINIT              (1U << 2)       /* PARPORT_CONTROL_INIT */
+#define DCR_AUTOFD             (1U << 1)       /* PARPORT_CONTROL_AUTOFD */
+#define DCR_STROBE             (1U << 0)       /* PARPORT_CONTROL_STROBE */
+
+/* ECP Configuration Register A */
+#define CNFGA_IRQ              (1U << 7)
+#define CNFGA_ID_MASK          ((1U << 6) | (1U << 5) | (1U << 4))
+#define CNFGA_ID_SHIFT         4
+#define CNFGA_ID_16            (00U << CNFGA_ID_SHIFT)
+#define CNFGA_ID_8             (01U << CNFGA_ID_SHIFT)
+#define CNFGA_ID_32            (02U << CNFGA_ID_SHIFT)
+/* #define CNFGA_reserved      (1U << 3) */
+#define CNFGA_nBYTEINTRANS     (1U << 2)
+#define CNFGA_PWORDLEFT                ((1U << 1) | (1U << 0))
+
+/* ECP Configuration Register B */
+#define CNFGB_COMPRESS         (1U << 7)
+#define CNFGB_INTRVAL          (1U << 6)
+#define CNFGB_IRQ_MASK         ((1U << 5) | (1U << 4) | (1U << 3))
+#define CNFGB_IRQ_SHIFT                3
+#define CNFGB_DMA_MASK         ((1U << 2) | (1U << 1) | (1U << 0))
+#define CNFGB_DMA_SHIFT                0
+
+/* Extended Control Register */
+#define ECR_MODE_MASK          ((1U << 7) | (1U << 6) | (1U << 5))
+#define ECR_MODE_SHIFT         5
+#define ECR_MODE_SPP           (00U << ECR_MODE_SHIFT)
+#define ECR_MODE_PS2           (01U << ECR_MODE_SHIFT)
+#define ECR_MODE_PPF           (02U << ECR_MODE_SHIFT)
+#define ECR_MODE_ECP           (03U << ECR_MODE_SHIFT)
+#define ECR_MODE_EPP           (04U << ECR_MODE_SHIFT)
+/* #define ECR_MODE_reserved   (05U << ECR_MODE_SHIFT) */
+#define ECR_MODE_TST           (06U << ECR_MODE_SHIFT)
+#define ECR_MODE_CFG           (07U << ECR_MODE_SHIFT)
+#define ECR_nERRINTR           (1U << 4)
+#define ECR_DMAEN              (1U << 3)
+#define ECR_SERVINTR           (1U << 2)
+#define ECR_F_FULL             (1U << 1)
+#define ECR_F_EMPTY            (1U << 0)
+
+/*--- Private data -----------------------------------------------------*/
+
+/**
+ * enum parport_ip32_irq_mode - operation mode of interrupt handler
+ * @PARPORT_IP32_IRQ_FWD:      forward interrupt to the upper parport layer
+ * @PARPORT_IP32_IRQ_HERE:     interrupt is handled locally
+ */
+enum parport_ip32_irq_mode { PARPORT_IP32_IRQ_FWD, PARPORT_IP32_IRQ_HERE };
+
+/**
+ * struct parport_ip32_private - private stuff for &struct parport
+ * @regs:              register addresses
+ * @dcr_cache:         cached contents of DCR
+ * @dcr_writable:      bit mask of writable DCR bits
+ * @pword:             number of bytes per PWord
+ * @fifo_depth:                number of PWords that FIFO will hold
+ * @readIntrThreshold: minimum number of PWords we can read
+ *                     if we get an interrupt
+ * @writeIntrThreshold:        minimum number of PWords we can write
+ *                     if we get an interrupt
+ * @irq_mode:          operation mode of interrupt handler for this port
+ * @irq_complete:      mutex used to wait for an interrupt to occur
+ */
+struct parport_ip32_private {
+       struct parport_ip32_regs        regs;
+       unsigned int                    dcr_cache;
+       unsigned int                    dcr_writable;
+       unsigned int                    pword;
+       unsigned int                    fifo_depth;
+       unsigned int                    readIntrThreshold;
+       unsigned int                    writeIntrThreshold;
+       enum parport_ip32_irq_mode      irq_mode;
+       struct completion               irq_complete;
+};
+
+/*--- Debug code -------------------------------------------------------*/
+
+/*
+ * pr_debug1 - print debug messages
+ *
+ * This is like pr_debug(), but is defined for %DEBUG_PARPORT_IP32 >= 1
+ */
+#if DEBUG_PARPORT_IP32 >= 1
+#      define pr_debug1(...)   printk(KERN_DEBUG __VA_ARGS__)
+#else /* DEBUG_PARPORT_IP32 < 1 */
+#      define pr_debug1(...)   do { } while (0)
+#endif
+
+/*
+ * pr_trace, pr_trace1 - trace function calls
+ * @p:         pointer to &struct parport
+ * @fmt:       printk format string
+ * @...:       parameters for format string
+ *
+ * Macros used to trace function calls.  The given string is formatted after
+ * function name.  pr_trace() uses pr_debug(), and pr_trace1() uses
+ * pr_debug1().  __pr_trace() is the low-level macro and is not to be used
+ * directly.
+ */
+#define __pr_trace(pr, p, fmt, ...)                                    \
+       pr("%s: %s" fmt "\n",                                           \
+          ({ const struct parport *__p = (p);                          \
+                  __p ? __p->name : "parport_ip32"; }),                \
+          __func__ , ##__VA_ARGS__)
+#define pr_trace(p, fmt, ...)  __pr_trace(pr_debug, p, fmt , ##__VA_ARGS__)
+#define pr_trace1(p, fmt, ...) __pr_trace(pr_debug1, p, fmt , ##__VA_ARGS__)
+
+/*
+ * __pr_probe, pr_probe - print message if @verbose_probing is true
+ * @p:         pointer to &struct parport
+ * @fmt:       printk format string
+ * @...:       parameters for format string
+ *
+ * For new lines, use pr_probe().  Use __pr_probe() for continued lines.
+ */
+#define __pr_probe(...)                                                        \
+       do { if (verbose_probing) printk(__VA_ARGS__); } while (0)
+#define pr_probe(p, fmt, ...)                                          \
+       __pr_probe(KERN_INFO PPIP32 "0x%lx: " fmt, (p)->base , ##__VA_ARGS__)
+
+/*
+ * parport_ip32_dump_state - print register status of parport
+ * @p:         pointer to &struct parport
+ * @str:       string to add in message
+ * @show_ecp_config:   shall we dump ECP configuration registers too?
+ *
+ * This function is only here for debugging purpose, and should be used with
+ * care.  Reading the parallel port registers may have undesired side effects.
+ * Especially if @show_ecp_config is true, the parallel port is resetted.
+ * This function is only defined if %DEBUG_PARPORT_IP32 >= 2.
+ */
+#if DEBUG_PARPORT_IP32 >= 2
+static void parport_ip32_dump_state(struct parport *p, char *str,
+                                   unsigned int show_ecp_config)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       unsigned int i;
+
+       printk(KERN_DEBUG PPIP32 "%s: state (%s):\n", p->name, str);
+       {
+               static const char ecr_modes[8][4] = {"SPP", "PS2", "PPF",
+                                                    "ECP", "EPP", "???",
+                                                    "TST", "CFG"};
+               unsigned int ecr = readb(priv->regs.ecr);
+               printk(KERN_DEBUG PPIP32 "    ecr=0x%02x", ecr);
+               printk(" %s",
+                      ecr_modes[(ecr & ECR_MODE_MASK) >> ECR_MODE_SHIFT]);
+               if (ecr & ECR_nERRINTR)
+                       printk(",nErrIntrEn");
+               if (ecr & ECR_DMAEN)
+                       printk(",dmaEn");
+               if (ecr & ECR_SERVINTR)
+                       printk(",serviceIntr");
+               if (ecr & ECR_F_FULL)
+                       printk(",f_full");
+               if (ecr & ECR_F_EMPTY)
+                       printk(",f_empty");
+               printk("\n");
+       }
+       if (show_ecp_config) {
+               unsigned int oecr, cnfgA, cnfgB;
+               oecr = readb(priv->regs.ecr);
+               writeb(ECR_MODE_PS2, priv->regs.ecr);
+               writeb(ECR_MODE_CFG, priv->regs.ecr);
+               cnfgA = readb(priv->regs.cnfgA);
+               cnfgB = readb(priv->regs.cnfgB);
+               writeb(ECR_MODE_PS2, priv->regs.ecr);
+               writeb(oecr, priv->regs.ecr);
+               printk(KERN_DEBUG PPIP32 "    cnfgA=0x%02x", cnfgA);
+               printk(" ISA-%s", (cnfgA & CNFGA_IRQ) ? "Level" : "Pulses");
+               switch (cnfgA & CNFGA_ID_MASK) {
+               case CNFGA_ID_8:
+                       printk(",8 bits");
+                       break;
+               case CNFGA_ID_16:
+                       printk(",16 bits");
+                       break;
+               case CNFGA_ID_32:
+                       printk(",32 bits");
+                       break;
+               default:
+                       printk(",unknown ID");
+                       break;
+               }
+               if (!(cnfgA & CNFGA_nBYTEINTRANS))
+                       printk(",ByteInTrans");
+               if ((cnfgA & CNFGA_ID_MASK) != CNFGA_ID_8)
+                       printk(",%d byte%s left", cnfgA & CNFGA_PWORDLEFT,
+                              ((cnfgA & CNFGA_PWORDLEFT) > 1) ? "s" : "");
+               printk("\n");
+               printk(KERN_DEBUG PPIP32 "    cnfgB=0x%02x", cnfgB);
+               printk(" irq=%u,dma=%u",
+                      (cnfgB & CNFGB_IRQ_MASK) >> CNFGB_IRQ_SHIFT,
+                      (cnfgB & CNFGB_DMA_MASK) >> CNFGB_DMA_SHIFT);
+               printk(",intrValue=%d", !!(cnfgB & CNFGB_INTRVAL));
+               if (cnfgB & CNFGB_COMPRESS)
+                       printk(",compress");
+               printk("\n");
+       }
+       for (i = 0; i < 2; i++) {
+               unsigned int dcr = i ? priv->dcr_cache : readb(priv->regs.dcr);
+               printk(KERN_DEBUG PPIP32 "    dcr(%s)=0x%02x",
+                      i ? "soft" : "hard", dcr);
+               printk(" %s", (dcr & DCR_DIR) ? "rev" : "fwd");
+               if (dcr & DCR_IRQ)
+                       printk(",ackIntEn");
+               if (!(dcr & DCR_SELECT))
+                       printk(",nSelectIn");
+               if (dcr & DCR_nINIT)
+                       printk(",nInit");
+               if (!(dcr & DCR_AUTOFD))
+                       printk(",nAutoFD");
+               if (!(dcr & DCR_STROBE))
+                       printk(",nStrobe");
+               printk("\n");
+       }
+#define sep (f++ ? ',' : ' ')
+       {
+               unsigned int f = 0;
+               unsigned int dsr = readb(priv->regs.dsr);
+               printk(KERN_DEBUG PPIP32 "    dsr=0x%02x", dsr);
+               if (!(dsr & DSR_nBUSY))
+                       printk("%cBusy", sep);
+               if (dsr & DSR_nACK)
+                       printk("%cnAck", sep);
+               if (dsr & DSR_PERROR)
+                       printk("%cPError", sep);
+               if (dsr & DSR_SELECT)
+                       printk("%cSelect", sep);
+               if (dsr & DSR_nFAULT)
+                       printk("%cnFault", sep);
+               if (!(dsr & DSR_nPRINT))
+                       printk("%c(Print)", sep);
+               if (dsr & DSR_TIMEOUT)
+                       printk("%cTimeout", sep);
+               printk("\n");
+       }
+#undef sep
+}
+#else /* DEBUG_PARPORT_IP32 < 2 */
+#define parport_ip32_dump_state(...)   do { } while (0)
+#endif
+
+/*
+ * CHECK_EXTRA_BITS - track and log extra bits
+ * @p:         pointer to &struct parport
+ * @b:         byte to inspect
+ * @m:         bit mask of authorized bits
+ *
+ * This is used to track and log extra bits that should not be there in
+ * parport_ip32_write_control() and parport_ip32_frob_control().  It is only
+ * defined if %DEBUG_PARPORT_IP32 >= 1.
+ */
+#if DEBUG_PARPORT_IP32 >= 1
+#define CHECK_EXTRA_BITS(p, b, m)                                      \
+       do {                                                            \
+               unsigned int __b = (b), __m = (m);                      \
+               if (__b & ~__m)                                         \
+                       pr_debug1(PPIP32 "%s: extra bits in %s(%s): "   \
+                                 "0x%02x/0x%02x\n",                    \
+                                 (p)->name, __func__, #b, __b, __m);   \
+       } while (0)
+#else /* DEBUG_PARPORT_IP32 < 1 */
+#define CHECK_EXTRA_BITS(...)  do { } while (0)
+#endif
+
+/*--- IP32 parallel port DMA operations --------------------------------*/
+
+/**
+ * struct parport_ip32_dma_data - private data needed for DMA operation
+ * @dir:       DMA direction (from or to device)
+ * @buf:       buffer physical address
+ * @len:       buffer length
+ * @next:      address of next bytes to DMA transfer
+ * @left:      number of bytes remaining
+ * @ctx:       next context to write (0: context_a; 1: context_b)
+ * @irq_on:    are the DMA IRQs currently enabled?
+ * @lock:      spinlock to protect access to the structure
+ */
+struct parport_ip32_dma_data {
+       enum dma_data_direction         dir;
+       dma_addr_t                      buf;
+       dma_addr_t                      next;
+       size_t                          len;
+       size_t                          left;
+       unsigned int                    ctx;
+       unsigned int                    irq_on;
+       spinlock_t                      lock;
+};
+static struct parport_ip32_dma_data parport_ip32_dma;
+
+/**
+ * parport_ip32_dma_setup_context - setup next DMA context
+ * @limit:     maximum data size for the context
+ *
+ * The alignment constraints must be verified in caller function, and the
+ * parameter @limit must be set accordingly.
+ */
+static void parport_ip32_dma_setup_context(unsigned int limit)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&parport_ip32_dma.lock, flags);
+       if (parport_ip32_dma.left > 0) {
+               /* Note: ctxreg is "volatile" here only because
+                * mace->perif.ctrl.parport.context_a and context_b are
+                * "volatile".  */
+               volatile u64 __iomem *ctxreg = (parport_ip32_dma.ctx == 0) ?
+                       &mace->perif.ctrl.parport.context_a :
+                       &mace->perif.ctrl.parport.context_b;
+               u64 count;
+               u64 ctxval;
+               if (parport_ip32_dma.left <= limit) {
+                       count = parport_ip32_dma.left;
+                       ctxval = MACEPAR_CONTEXT_LASTFLAG;
+               } else {
+                       count = limit;
+                       ctxval = 0;
+               }
+
+               pr_trace(NULL,
+                        "(%u): 0x%04x:0x%04x, %u -> %u%s",
+                        limit,
+                        (unsigned int)parport_ip32_dma.buf,
+                        (unsigned int)parport_ip32_dma.next,
+                        (unsigned int)count,
+                        parport_ip32_dma.ctx, ctxval ? "*" : "");
+
+               ctxval |= parport_ip32_dma.next &
+                       MACEPAR_CONTEXT_BASEADDR_MASK;
+               ctxval |= ((count - 1) << MACEPAR_CONTEXT_DATALEN_SHIFT) &
+                       MACEPAR_CONTEXT_DATALEN_MASK;
+               writeq(ctxval, ctxreg);
+               parport_ip32_dma.next += count;
+               parport_ip32_dma.left -= count;
+               parport_ip32_dma.ctx ^= 1U;
+       }
+       /* If there is nothing more to send, disable IRQs to avoid to
+        * face an IRQ storm which can lock the machine.  Disable them
+        * only once. */
+       if (parport_ip32_dma.left == 0 && parport_ip32_dma.irq_on) {
+               pr_debug(PPIP32 "IRQ off (ctx)\n");
+               disable_irq_nosync(MACEISA_PAR_CTXA_IRQ);
+               disable_irq_nosync(MACEISA_PAR_CTXB_IRQ);
+               parport_ip32_dma.irq_on = 0;
+       }
+       spin_unlock_irqrestore(&parport_ip32_dma.lock, flags);
+}
+
+/**
+ * parport_ip32_dma_interrupt - DMA interrupt handler
+ * @irq:       interrupt number
+ * @dev_id:    unused
+ * @regs:      pointer to &struct pt_regs
+ */
+static irqreturn_t parport_ip32_dma_interrupt(int irq, void *dev_id,
+                                             struct pt_regs *regs)
+{
+       if (parport_ip32_dma.left)
+               pr_trace(NULL, "(%d): ctx=%d", irq, parport_ip32_dma.ctx);
+       parport_ip32_dma_setup_context(MACEPAR_CONTEXT_DATA_BOUND);
+       return IRQ_HANDLED;
+}
+
+#if DEBUG_PARPORT_IP32
+static irqreturn_t parport_ip32_merr_interrupt(int irq, void *dev_id,
+                                              struct pt_regs *regs)
+{
+       pr_trace1(NULL, "(%d)", irq);
+       return IRQ_HANDLED;
+}
+#endif
+
+/**
+ * parport_ip32_dma_start - begins a DMA transfer
+ * @dir:       DMA direction: DMA_TO_DEVICE or DMA_FROM_DEVICE
+ * @addr:      pointer to data buffer
+ * @count:     buffer size
+ *
+ * Calls to parport_ip32_dma_start() and parport_ip32_dma_stop() must be
+ * correctly balanced.
+ */
+static int parport_ip32_dma_start(enum dma_data_direction dir,
+                                 void *addr, size_t count)
+{
+       unsigned int limit;
+       u64 ctrl;
+
+       pr_trace(NULL, "(%d, %lu)", dir, (unsigned long)count);
+
+       /* FIXME - add support for DMA_FROM_DEVICE.  In this case, buffer must
+        * be 64 bytes aligned. */
+       BUG_ON(dir != DMA_TO_DEVICE);
+
+       /* Reset DMA controller */
+       ctrl = MACEPAR_CTLSTAT_RESET;
+       writeq(ctrl, &mace->perif.ctrl.parport.cntlstat);
+
+       /* DMA IRQs should normally be enabled */
+       if (!parport_ip32_dma.irq_on) {
+               WARN_ON(1);
+               enable_irq(MACEISA_PAR_CTXA_IRQ);
+               enable_irq(MACEISA_PAR_CTXB_IRQ);
+               parport_ip32_dma.irq_on = 1;
+       }
+
+       /* Prepare DMA pointers */
+       parport_ip32_dma.dir = dir;
+       parport_ip32_dma.buf = dma_map_single(NULL, addr, count, dir);
+       parport_ip32_dma.len = count;
+       parport_ip32_dma.next = parport_ip32_dma.buf;
+       parport_ip32_dma.left = parport_ip32_dma.len;
+       parport_ip32_dma.ctx = 0;
+
+       /* Setup DMA direction and first two contexts */
+       ctrl = (dir == DMA_TO_DEVICE) ? 0 : MACEPAR_CTLSTAT_DIRECTION;
+       writeq(ctrl, &mace->perif.ctrl.parport.cntlstat);
+       /* Single transfer should not cross a 4K page boundary */
+       limit = MACEPAR_CONTEXT_DATA_BOUND -
+               (parport_ip32_dma.next & (MACEPAR_CONTEXT_DATA_BOUND - 1));
+       parport_ip32_dma_setup_context(limit);
+       parport_ip32_dma_setup_context(MACEPAR_CONTEXT_DATA_BOUND);
+
+       /* Real start of DMA transfer */
+       ctrl |= MACEPAR_CTLSTAT_ENABLE;
+       writeq(ctrl, &mace->perif.ctrl.parport.cntlstat);
+
+       return 0;
+}
+
+/**
+ * parport_ip32_dma_stop - ends a running DMA transfer
+ *
+ * Calls to parport_ip32_dma_start() and parport_ip32_dma_stop() must be
+ * correctly balanced.
+ */
+static void parport_ip32_dma_stop(void)
+{
+       u64 ctx_a;
+       u64 ctx_b;
+       u64 ctrl;
+       u64 diag;
+       size_t res[2];  /* {[0] = res_a, [1] = res_b} */
+
+       pr_trace(NULL, "()");
+
+       /* Disable IRQs */
+       spin_lock_irq(&parport_ip32_dma.lock);
+       if (parport_ip32_dma.irq_on) {
+               pr_debug(PPIP32 "IRQ off (stop)\n");
+               disable_irq_nosync(MACEISA_PAR_CTXA_IRQ);
+               disable_irq_nosync(MACEISA_PAR_CTXB_IRQ);
+               parport_ip32_dma.irq_on = 0;
+       }
+       spin_unlock_irq(&parport_ip32_dma.lock);
+       /* Force IRQ synchronization, even if the IRQs were disabled
+        * elsewhere. */
+       synchronize_irq(MACEISA_PAR_CTXA_IRQ);
+       synchronize_irq(MACEISA_PAR_CTXB_IRQ);
+
+       /* Stop DMA transfer */
+       ctrl = readq(&mace->perif.ctrl.parport.cntlstat);
+       ctrl &= ~MACEPAR_CTLSTAT_ENABLE;
+       writeq(ctrl, &mace->perif.ctrl.parport.cntlstat);
+
+       /* Adjust residue (parport_ip32_dma.left) */
+       ctx_a = readq(&mace->perif.ctrl.parport.context_a);
+       ctx_b = readq(&mace->perif.ctrl.parport.context_b);
+       ctrl = readq(&mace->perif.ctrl.parport.cntlstat);
+       diag = readq(&mace->perif.ctrl.parport.diagnostic);
+       res[0] = (ctrl & MACEPAR_CTLSTAT_CTXA_VALID) ?
+               1 + ((ctx_a & MACEPAR_CONTEXT_DATALEN_MASK) >>
+                    MACEPAR_CONTEXT_DATALEN_SHIFT) :
+               0;
+       res[1] = (ctrl & MACEPAR_CTLSTAT_CTXB_VALID) ?
+               1 + ((ctx_b & MACEPAR_CONTEXT_DATALEN_MASK) >>
+                    MACEPAR_CONTEXT_DATALEN_SHIFT) :
+               0;
+       if (diag & MACEPAR_DIAG_DMACTIVE)
+               res[(diag & MACEPAR_DIAG_CTXINUSE) != 0] =
+                       1 + ((diag & MACEPAR_DIAG_CTRMASK) >>
+                            MACEPAR_DIAG_CTRSHIFT);
+       parport_ip32_dma.left += res[0] + res[1];
+
+       /* Reset DMA controller, and re-enable IRQs */
+       ctrl = MACEPAR_CTLSTAT_RESET;
+       writeq(ctrl, &mace->perif.ctrl.parport.cntlstat);
+       pr_debug(PPIP32 "IRQ on (stop)\n");
+       enable_irq(MACEISA_PAR_CTXA_IRQ);
+       enable_irq(MACEISA_PAR_CTXB_IRQ);
+       parport_ip32_dma.irq_on = 1;
+
+       dma_unmap_single(NULL, parport_ip32_dma.buf, parport_ip32_dma.len,
+                        parport_ip32_dma.dir);
+}
+
+/**
+ * parport_ip32_dma_get_residue - get residue from last DMA transfer
+ *
+ * Returns the number of bytes remaining from last DMA transfer.
+ */
+static inline size_t parport_ip32_dma_get_residue(void)
+{
+       return parport_ip32_dma.left;
+}
+
+/**
+ * parport_ip32_dma_register - initialize DMA engine
+ *
+ * Returns zero for success.
+ */
+static int parport_ip32_dma_register(void)
+{
+       int err;
+
+       spin_lock_init(&parport_ip32_dma.lock);
+       parport_ip32_dma.irq_on = 1;
+
+       /* Reset DMA controller */
+       writeq(MACEPAR_CTLSTAT_RESET, &mace->perif.ctrl.parport.cntlstat);
+
+       /* Request IRQs */
+       err = request_irq(MACEISA_PAR_CTXA_IRQ, parport_ip32_dma_interrupt,
+                         0, "parport_ip32", NULL);
+       if (err)
+               goto fail_a;
+       err = request_irq(MACEISA_PAR_CTXB_IRQ, parport_ip32_dma_interrupt,
+                         0, "parport_ip32", NULL);
+       if (err)
+               goto fail_b;
+#if DEBUG_PARPORT_IP32
+       /* FIXME - what is this IRQ for? */
+       err = request_irq(MACEISA_PAR_MERR_IRQ, parport_ip32_merr_interrupt,
+                         0, "parport_ip32", NULL);
+       if (err)
+               goto fail_merr;
+#endif
+       return 0;
+
+#if DEBUG_PARPORT_IP32
+fail_merr:
+       free_irq(MACEISA_PAR_CTXB_IRQ, NULL);
+#endif
+fail_b:
+       free_irq(MACEISA_PAR_CTXA_IRQ, NULL);
+fail_a:
+       return err;
+}
+
+/**
+ * parport_ip32_dma_unregister - release and free resources for DMA engine
+ */
+static void parport_ip32_dma_unregister(void)
+{
+#if DEBUG_PARPORT_IP32
+       free_irq(MACEISA_PAR_MERR_IRQ, NULL);
+#endif
+       free_irq(MACEISA_PAR_CTXB_IRQ, NULL);
+       free_irq(MACEISA_PAR_CTXA_IRQ, NULL);
+}
+
+/*--- Interrupt handlers and associates --------------------------------*/
+
+/**
+ * parport_ip32_wakeup - wakes up code waiting for an interrupt
+ * @p:         pointer to &struct parport
+ */
+static inline void parport_ip32_wakeup(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       complete(&priv->irq_complete);
+}
+
+/**
+ * parport_ip32_interrupt - interrupt handler
+ * @irq:       interrupt number
+ * @dev_id:    pointer to &struct parport
+ * @regs:      pointer to &struct pt_regs
+ *
+ * Caught interrupts are forwarded to the upper parport layer if IRQ_mode is
+ * %PARPORT_IP32_IRQ_FWD.
+ */
+static irqreturn_t parport_ip32_interrupt(int irq, void *dev_id,
+                                         struct pt_regs *regs)
+{
+       struct parport * const p = dev_id;
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       enum parport_ip32_irq_mode irq_mode = priv->irq_mode;
+       switch (irq_mode) {
+       case PARPORT_IP32_IRQ_FWD:
+               parport_generic_irq(irq, p, regs);
+               break;
+       case PARPORT_IP32_IRQ_HERE:
+               parport_ip32_wakeup(p);
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+/*--- Some utility function to manipulate ECR register -----------------*/
+
+/**
+ * parport_ip32_read_econtrol - read contents of the ECR register
+ * @p:         pointer to &struct parport
+ */
+static inline unsigned int parport_ip32_read_econtrol(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return readb(priv->regs.ecr);
+}
+
+/**
+ * parport_ip32_write_econtrol - write new contents to the ECR register
+ * @p:         pointer to &struct parport
+ * @c:         new value to write
+ */
+static inline void parport_ip32_write_econtrol(struct parport *p,
+                                              unsigned int c)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       writeb(c, priv->regs.ecr);
+}
+
+/**
+ * parport_ip32_frob_econtrol - change bits from the ECR register
+ * @p:         pointer to &struct parport
+ * @mask:      bit mask of bits to change
+ * @val:       new value for changed bits
+ *
+ * Read from the ECR, mask out the bits in @mask, exclusive-or with the bits
+ * in @val, and write the result to the ECR.
+ */
+static inline void parport_ip32_frob_econtrol(struct parport *p,
+                                             unsigned int mask,
+                                             unsigned int val)
+{
+       unsigned int c;
+       c = (parport_ip32_read_econtrol(p) & ~mask) ^ val;
+       parport_ip32_write_econtrol(p, c);
+}
+
+/**
+ * parport_ip32_set_mode - change mode of ECP port
+ * @p:         pointer to &struct parport
+ * @mode:      new mode to write in ECR
+ *
+ * ECR is reset in a sane state (interrupts and DMA disabled), and placed in
+ * mode @mode.  Go through PS2 mode if needed.
+ */
+static void parport_ip32_set_mode(struct parport *p, unsigned int mode)
+{
+       unsigned int omode;
+
+       mode &= ECR_MODE_MASK;
+       omode = parport_ip32_read_econtrol(p) & ECR_MODE_MASK;
+
+       if (!(mode == ECR_MODE_SPP || mode == ECR_MODE_PS2
+             || omode == ECR_MODE_SPP || omode == ECR_MODE_PS2)) {
+               /* We have to go through PS2 mode */
+               unsigned int ecr = ECR_MODE_PS2 | ECR_nERRINTR | ECR_SERVINTR;
+               parport_ip32_write_econtrol(p, ecr);
+       }
+       parport_ip32_write_econtrol(p, mode | ECR_nERRINTR | ECR_SERVINTR);
+}
+
+/*--- Basic functions needed for parport -------------------------------*/
+
+/**
+ * parport_ip32_read_data - return current contents of the DATA register
+ * @p:         pointer to &struct parport
+ */
+static inline unsigned char parport_ip32_read_data(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return readb(priv->regs.data);
+}
+
+/**
+ * parport_ip32_write_data - set new contents for the DATA register
+ * @p:         pointer to &struct parport
+ * @d:         new value to write
+ */
+static inline void parport_ip32_write_data(struct parport *p, unsigned char d)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       writeb(d, priv->regs.data);
+}
+
+/**
+ * parport_ip32_read_status - return current contents of the DSR register
+ * @p:         pointer to &struct parport
+ */
+static inline unsigned char parport_ip32_read_status(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return readb(priv->regs.dsr);
+}
+
+/**
+ * __parport_ip32_read_control - return cached contents of the DCR register
+ * @p:         pointer to &struct parport
+ */
+static inline unsigned int __parport_ip32_read_control(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return priv->dcr_cache; /* use soft copy */
+}
+
+/**
+ * __parport_ip32_write_control - set new contents for the DCR register
+ * @p:         pointer to &struct parport
+ * @c:         new value to write
+ */
+static inline void __parport_ip32_write_control(struct parport *p,
+                                               unsigned int c)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       CHECK_EXTRA_BITS(p, c, priv->dcr_writable);
+       c &= priv->dcr_writable; /* only writable bits */
+       writeb(c, priv->regs.dcr);
+       priv->dcr_cache = c;            /* update soft copy */
+}
+
+/**
+ * __parport_ip32_frob_control - change bits from the DCR register
+ * @p:         pointer to &struct parport
+ * @mask:      bit mask of bits to change
+ * @val:       new value for changed bits
+ *
+ * This is equivalent to read from the DCR, mask out the bits in @mask,
+ * exclusive-or with the bits in @val, and write the result to the DCR.
+ * Actually, the cached contents of the DCR is used.
+ */
+static inline void __parport_ip32_frob_control(struct parport *p,
+                                              unsigned int mask,
+                                              unsigned int val)
+{
+       unsigned int c;
+       c = (__parport_ip32_read_control(p) & ~mask) ^ val;
+       __parport_ip32_write_control(p, c);
+}
+
+/**
+ * parport_ip32_read_control - return cached contents of the DCR register
+ * @p:         pointer to &struct parport
+ *
+ * The return value is masked so as to only return the value of %DCR_STROBE,
+ * %DCR_AUTOFD, %DCR_nINIT, and %DCR_SELECT.
+ */
+static inline unsigned char parport_ip32_read_control(struct parport *p)
+{
+       const unsigned int rm =
+               DCR_STROBE | DCR_AUTOFD | DCR_nINIT | DCR_SELECT;
+       return __parport_ip32_read_control(p) & rm;
+}
+
+/**
+ * parport_ip32_write_control - set new contents for the DCR register
+ * @p:         pointer to &struct parport
+ * @c:         new value to write
+ *
+ * The value is masked so as to only change the value of %DCR_STROBE,
+ * %DCR_AUTOFD, %DCR_nINIT, and %DCR_SELECT.
+ */
+static inline void parport_ip32_write_control(struct parport *p,
+                                             unsigned char c)
+{
+       const unsigned int wm =
+               DCR_STROBE | DCR_AUTOFD | DCR_nINIT | DCR_SELECT;
+       CHECK_EXTRA_BITS(p, c, wm);
+       __parport_ip32_frob_control(p, wm, c & wm);
+}
+
+/**
+ * parport_ip32_frob_control - change bits from the DCR register
+ * @p:         pointer to &struct parport
+ * @mask:      bit mask of bits to change
+ * @val:       new value for changed bits
+ *
+ * This differs from __parport_ip32_frob_control() in that it only allows to
+ * change the value of %DCR_STROBE, %DCR_AUTOFD, %DCR_nINIT, and %DCR_SELECT.
+ */
+static inline unsigned char parport_ip32_frob_control(struct parport *p,
+                                                     unsigned char mask,
+                                                     unsigned char val)
+{
+       const unsigned int wm =
+               DCR_STROBE | DCR_AUTOFD | DCR_nINIT | DCR_SELECT;
+       CHECK_EXTRA_BITS(p, mask, wm);
+       CHECK_EXTRA_BITS(p, val, wm);
+       __parport_ip32_frob_control(p, mask & wm, val & wm);
+       return parport_ip32_read_control(p);
+}
+
+/**
+ * parport_ip32_disable_irq - disable interrupts on the rising edge of nACK
+ * @p:         pointer to &struct parport
+ */
+static inline void parport_ip32_disable_irq(struct parport *p)
+{
+       __parport_ip32_frob_control(p, DCR_IRQ, 0);
+}
+
+/**
+ * parport_ip32_enable_irq - enable interrupts on the rising edge of nACK
+ * @p:         pointer to &struct parport
+ */
+static inline void parport_ip32_enable_irq(struct parport *p)
+{
+       __parport_ip32_frob_control(p, DCR_IRQ, DCR_IRQ);
+}
+
+/**
+ * parport_ip32_data_forward - enable host-to-peripheral communications
+ * @p:         pointer to &struct parport
+ *
+ * Enable the data line drivers, for 8-bit host-to-peripheral communications.
+ */
+static inline void parport_ip32_data_forward(struct parport *p)
+{
+       __parport_ip32_frob_control(p, DCR_DIR, 0);
+}
+
+/**
+ * parport_ip32_data_reverse - enable peripheral-to-host communications
+ * @p:         pointer to &struct parport
+ *
+ * Place the data bus in a high impedance state, if @p->modes has the
+ * PARPORT_MODE_TRISTATE bit set.
+ */
+static inline void parport_ip32_data_reverse(struct parport *p)
+{
+       __parport_ip32_frob_control(p, DCR_DIR, DCR_DIR);
+}
+
+/**
+ * parport_ip32_init_state - for core parport code
+ * @dev:       pointer to &struct pardevice
+ * @s:         pointer to &struct parport_state to initialize
+ */
+static void parport_ip32_init_state(struct pardevice *dev,
+                                   struct parport_state *s)
+{
+       s->u.ip32.dcr = DCR_SELECT | DCR_nINIT;
+       s->u.ip32.ecr = ECR_MODE_PS2 | ECR_nERRINTR | ECR_SERVINTR;
+}
+
+/**
+ * parport_ip32_save_state - for core parport code
+ * @p:         pointer to &struct parport
+ * @s:         pointer to &struct parport_state to save state to
+ */
+static void parport_ip32_save_state(struct parport *p,
+                                   struct parport_state *s)
+{
+       s->u.ip32.dcr = __parport_ip32_read_control(p);
+       s->u.ip32.ecr = parport_ip32_read_econtrol(p);
+}
+
+/**
+ * parport_ip32_restore_state - for core parport code
+ * @p:         pointer to &struct parport
+ * @s:         pointer to &struct parport_state to restore state from
+ */
+static void parport_ip32_restore_state(struct parport *p,
+                                      struct parport_state *s)
+{
+       parport_ip32_set_mode(p, s->u.ip32.ecr & ECR_MODE_MASK);
+       parport_ip32_write_econtrol(p, s->u.ip32.ecr);
+       __parport_ip32_write_control(p, s->u.ip32.dcr);
+}
+
+/*--- EPP mode functions -----------------------------------------------*/
+
+/**
+ * parport_ip32_clear_epp_timeout - clear Timeout bit in EPP mode
+ * @p:         pointer to &struct parport
+ *
+ * Returns 1 if the Timeout bit is clear, and 0 otherwise.
+ */
+static unsigned int parport_ip32_clear_epp_timeout(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       unsigned int cleared;
+
+       if (!(parport_ip32_read_status(p) & DSR_TIMEOUT))
+               cleared = 1;
+       else {
+               unsigned int r;
+               /* To clear timeout some chips require double read */
+               parport_ip32_read_status(p);
+               r = parport_ip32_read_status(p);
+               /* Some reset by writing 1 */
+               writeb(r | DSR_TIMEOUT, priv->regs.dsr);
+               /* Others by writing 0 */
+               writeb(r & ~DSR_TIMEOUT, priv->regs.dsr);
+
+               r = parport_ip32_read_status(p);
+               cleared = !(r & DSR_TIMEOUT);
+       }
+
+       pr_trace(p, "(): %s", cleared ? "cleared" : "failed");
+       return cleared;
+}
+
+/**
+ * parport_ip32_epp_read - generic EPP read function
+ * @eppreg:    I/O register to read from
+ * @p:         pointer to &struct parport
+ * @buf:       buffer to store read data
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_read(void __iomem *eppreg,
+                                   struct parport *p, void *buf,
+                                   size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       size_t got;
+       parport_ip32_set_mode(p, ECR_MODE_EPP);
+       parport_ip32_data_reverse(p);
+       parport_ip32_write_control(p, DCR_nINIT);
+       if ((flags & PARPORT_EPP_FAST) && (len > 1)) {
+               readsb(eppreg, buf, len);
+               if (readb(priv->regs.dsr) & DSR_TIMEOUT) {
+                       parport_ip32_clear_epp_timeout(p);
+                       return -EIO;
+               }
+               got = len;
+       } else {
+               u8 *bufp = buf;
+               for (got = 0; got < len; got++) {
+                       *bufp++ = readb(eppreg);
+                       if (readb(priv->regs.dsr) & DSR_TIMEOUT) {
+                               parport_ip32_clear_epp_timeout(p);
+                               break;
+                       }
+               }
+       }
+       parport_ip32_data_forward(p);
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       return got;
+}
+
+/**
+ * parport_ip32_epp_write - generic EPP write function
+ * @eppreg:    I/O register to write to
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_write(void __iomem *eppreg,
+                                    struct parport *p, const void *buf,
+                                    size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       size_t written;
+       parport_ip32_set_mode(p, ECR_MODE_EPP);
+       parport_ip32_data_forward(p);
+       parport_ip32_write_control(p, DCR_nINIT);
+       if ((flags & PARPORT_EPP_FAST) && (len > 1)) {
+               writesb(eppreg, buf, len);
+               if (readb(priv->regs.dsr) & DSR_TIMEOUT) {
+                       parport_ip32_clear_epp_timeout(p);
+                       return -EIO;
+               }
+               written = len;
+       } else {
+               const u8 *bufp = buf;
+               for (written = 0; written < len; written++) {
+                       writeb(*bufp++, eppreg);
+                       if (readb(priv->regs.dsr) & DSR_TIMEOUT) {
+                               parport_ip32_clear_epp_timeout(p);
+                               break;
+                       }
+               }
+       }
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       return written;
+}
+
+/**
+ * parport_ip32_epp_read_data - read a block of data in EPP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer to store read data
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_read_data(struct parport *p, void *buf,
+                                        size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return parport_ip32_epp_read(priv->regs.eppData0, p, buf, len, flags);
+}
+
+/**
+ * parport_ip32_epp_write_data - write a block of data in EPP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_write_data(struct parport *p, const void *buf,
+                                         size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return parport_ip32_epp_write(priv->regs.eppData0, p, buf, len, flags);
+}
+
+/**
+ * parport_ip32_epp_read_addr - read a block of addresses in EPP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer to store read data
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_read_addr(struct parport *p, void *buf,
+                                        size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return parport_ip32_epp_read(priv->regs.eppAddr, p, buf, len, flags);
+}
+
+/**
+ * parport_ip32_epp_write_addr - write a block of addresses in EPP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ * @flags:     may be PARPORT_EPP_FAST
+ */
+static size_t parport_ip32_epp_write_addr(struct parport *p, const void *buf,
+                                         size_t len, int flags)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       return parport_ip32_epp_write(priv->regs.eppAddr, p, buf, len, flags);
+}
+
+/*--- ECP mode functions (FIFO) ----------------------------------------*/
+
+/**
+ * parport_ip32_fifo_wait_break - check if the waiting function should return
+ * @p:         pointer to &struct parport
+ * @expire:    timeout expiring date, in jiffies
+ *
+ * parport_ip32_fifo_wait_break() checks if the waiting function should return
+ * immediately or not.  The break conditions are:
+ *     - expired timeout;
+ *     - a pending signal;
+ *     - nFault asserted low.
+ * This function also calls cond_resched().
+ */
+static unsigned int parport_ip32_fifo_wait_break(struct parport *p,
+                                                unsigned long expire)
+{
+       cond_resched();
+       if (time_after(jiffies, expire)) {
+               pr_debug1(PPIP32 "%s: FIFO write timed out\n", p->name);
+               return 1;
+       }
+       if (signal_pending(current)) {
+               pr_debug1(PPIP32 "%s: Signal pending\n", p->name);
+               return 1;
+       }
+       if (!(parport_ip32_read_status(p) & DSR_nFAULT)) {
+               pr_debug1(PPIP32 "%s: nFault asserted low\n", p->name);
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * parport_ip32_fwp_wait_polling - wait for FIFO to empty (polling)
+ * @p:         pointer to &struct parport
+ *
+ * Returns the number of bytes that can safely be written in the FIFO.  A
+ * return value of zero means that the calling function should terminate as
+ * fast as possible.
+ */
+static unsigned int parport_ip32_fwp_wait_polling(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport * const physport = p->physport;
+       unsigned long expire;
+       unsigned int count;
+       unsigned int ecr;
+
+       expire = jiffies + physport->cad->timeout;
+       count = 0;
+       while (1) {
+               if (parport_ip32_fifo_wait_break(p, expire))
+                       break;
+
+               /* Check FIFO state.  We do nothing when the FIFO is nor full,
+                * nor empty.  It appears that the FIFO full bit is not always
+                * reliable, the FIFO state is sometimes wrongly reported, and
+                * the chip gets confused if we give it another byte. */
+               ecr = parport_ip32_read_econtrol(p);
+               if (ecr & ECR_F_EMPTY) {
+                       /* FIFO is empty, fill it up */
+                       count = priv->fifo_depth;
+                       break;
+               }
+
+               /* Wait a moment... */
+               udelay(FIFO_POLLING_INTERVAL);
+       } /* while (1) */
+
+       return count;
+}
+
+/**
+ * parport_ip32_fwp_wait_interrupt - wait for FIFO to empty (interrupt-driven)
+ * @p:         pointer to &struct parport
+ *
+ * Returns the number of bytes that can safely be written in the FIFO.  A
+ * return value of zero means that the calling function should terminate as
+ * fast as possible.
+ */
+static unsigned int parport_ip32_fwp_wait_interrupt(struct parport *p)
+{
+       static unsigned int lost_interrupt = 0;
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport * const physport = p->physport;
+       unsigned long nfault_timeout;
+       unsigned long expire;
+       unsigned int count;
+       unsigned int ecr;
+
+       nfault_timeout = min((unsigned long)physport->cad->timeout,
+                            msecs_to_jiffies(FIFO_NFAULT_TIMEOUT));
+       expire = jiffies + physport->cad->timeout;
+       count = 0;
+       while (1) {
+               if (parport_ip32_fifo_wait_break(p, expire))
+                       break;
+
+               /* Initialize mutex used to take interrupts into account */
+               INIT_COMPLETION(priv->irq_complete);
+
+               /* Enable serviceIntr */
+               parport_ip32_frob_econtrol(p, ECR_SERVINTR, 0);
+
+               /* Enabling serviceIntr while the FIFO is empty does not
+                * always generate an interrupt, so check for emptiness
+                * now. */
+               ecr = parport_ip32_read_econtrol(p);
+               if (!(ecr & ECR_F_EMPTY)) {
+                       /* FIFO is not empty: wait for an interrupt or a
+                        * timeout to occur */
+                       wait_for_completion_interruptible_timeout(
+                               &priv->irq_complete, nfault_timeout);
+                       ecr = parport_ip32_read_econtrol(p);
+                       if ((ecr & ECR_F_EMPTY) && !(ecr & ECR_SERVINTR)
+                           && !lost_interrupt) {
+                               printk(KERN_WARNING PPIP32
+                                      "%s: lost interrupt in %s\n",
+                                      p->name, __func__);
+                               lost_interrupt = 1;
+                       }
+               }
+
+               /* Disable serviceIntr */
+               parport_ip32_frob_econtrol(p, ECR_SERVINTR, ECR_SERVINTR);
+
+               /* Check FIFO state */
+               if (ecr & ECR_F_EMPTY) {
+                       /* FIFO is empty, fill it up */
+                       count = priv->fifo_depth;
+                       break;
+               } else if (ecr & ECR_SERVINTR) {
+                       /* FIFO is not empty, but we know that can safely push
+                        * writeIntrThreshold bytes into it */
+                       count = priv->writeIntrThreshold;
+                       break;
+               }
+               /* FIFO is not empty, and we did not get any interrupt.
+                * Either it's time to check for nFault, or a signal is
+                * pending.  This is verified in
+                * parport_ip32_fifo_wait_break(), so we continue the loop. */
+       } /* while (1) */
+
+       return count;
+}
+
+/**
+ * parport_ip32_fifo_write_block_pio - write a block of data (PIO mode)
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ *
+ * Uses PIO to write the contents of the buffer @buf into the parallel port
+ * FIFO.  Returns the number of bytes that were actually written.  It can work
+ * with or without the help of interrupts.  The parallel port must be
+ * correctly initialized before calling parport_ip32_fifo_write_block_pio().
+ */
+static size_t parport_ip32_fifo_write_block_pio(struct parport *p,
+                                               const void *buf, size_t len)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       const u8 *bufp = buf;
+       size_t left = len;
+
+       priv->irq_mode = PARPORT_IP32_IRQ_HERE;
+
+       while (left > 0) {
+               unsigned int count;
+
+               count = (p->irq == PARPORT_IRQ_NONE) ?
+                       parport_ip32_fwp_wait_polling(p) :
+                       parport_ip32_fwp_wait_interrupt(p);
+               if (count == 0)
+                       break;  /* Transmission should be stopped */
+               if (count > left)
+                       count = left;
+               if (count == 1) {
+                       writeb(*bufp, priv->regs.fifo);
+                       bufp++, left--;
+               } else {
+                       writesb(priv->regs.fifo, bufp, count);
+                       bufp += count, left -= count;
+               }
+       }
+
+       priv->irq_mode = PARPORT_IP32_IRQ_FWD;
+
+       return len - left;
+}
+
+/**
+ * parport_ip32_fifo_write_block_dma - write a block of data (DMA mode)
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ *
+ * Uses DMA to write the contents of the buffer @buf into the parallel port
+ * FIFO.  Returns the number of bytes that were actually written.  The
+ * parallel port must be correctly initialized before calling
+ * parport_ip32_fifo_write_block_dma().
+ */
+static size_t parport_ip32_fifo_write_block_dma(struct parport *p,
+                                               const void *buf, size_t len)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport * const physport = p->physport;
+       unsigned long nfault_timeout;
+       unsigned long expire;
+       size_t written;
+       unsigned int ecr;
+
+       priv->irq_mode = PARPORT_IP32_IRQ_HERE;
+
+       parport_ip32_dma_start(DMA_TO_DEVICE, (void *)buf, len);
+       INIT_COMPLETION(priv->irq_complete);
+       parport_ip32_frob_econtrol(p, ECR_DMAEN | ECR_SERVINTR, ECR_DMAEN);
+
+       nfault_timeout = min((unsigned long)physport->cad->timeout,
+                            msecs_to_jiffies(FIFO_NFAULT_TIMEOUT));
+       expire = jiffies + physport->cad->timeout;
+       while (1) {
+               if (parport_ip32_fifo_wait_break(p, expire))
+                       break;
+               wait_for_completion_interruptible_timeout(&priv->irq_complete,
+                                                         nfault_timeout);
+               ecr = parport_ip32_read_econtrol(p);
+               if (ecr & ECR_SERVINTR)
+                       break;  /* DMA transfer just finished */
+       }
+       parport_ip32_dma_stop();
+       written = len - parport_ip32_dma_get_residue();
+
+       priv->irq_mode = PARPORT_IP32_IRQ_FWD;
+
+       return written;
+}
+
+/**
+ * parport_ip32_fifo_write_block - write a block of data
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ *
+ * Uses PIO or DMA to write the contents of the buffer @buf into the parallel
+ * p FIFO.  Returns the number of bytes that were actually written.
+ */
+static size_t parport_ip32_fifo_write_block(struct parport *p,
+                                           const void *buf, size_t len)
+{
+       size_t written = 0;
+       if (len)
+               /* FIXME - Maybe some threshold value should be set for @len
+                * under which we revert to PIO mode? */
+               written = (p->modes & PARPORT_MODE_DMA) ?
+                       parport_ip32_fifo_write_block_dma(p, buf, len) :
+                       parport_ip32_fifo_write_block_pio(p, buf, len);
+       return written;
+}
+
+/**
+ * parport_ip32_drain_fifo - wait for FIFO to empty
+ * @p:         pointer to &struct parport
+ * @timeout:   timeout, in jiffies
+ *
+ * This function waits for FIFO to empty.  It returns 1 when FIFO is empty, or
+ * 0 if the timeout @timeout is reached before, or if a signal is pending.
+ */
+static unsigned int parport_ip32_drain_fifo(struct parport *p,
+                                           unsigned long timeout)
+{
+       unsigned long expire = jiffies + timeout;
+       unsigned int polling_interval;
+       unsigned int counter;
+
+       /* Busy wait for approx. 200us */
+       for (counter = 0; counter < 40; counter++) {
+               if (parport_ip32_read_econtrol(p) & ECR_F_EMPTY)
+                       break;
+               if (time_after(jiffies, expire))
+                       break;
+               if (signal_pending(current))
+                       break;
+               udelay(5);
+       }
+       /* Poll slowly.  Polling interval starts with 1 millisecond, and is
+        * increased exponentially until 128.  */
+       polling_interval = 1; /* msecs */
+       while (!(parport_ip32_read_econtrol(p) & ECR_F_EMPTY)) {
+               if (time_after_eq(jiffies, expire))
+                       break;
+               msleep_interruptible(polling_interval);
+               if (signal_pending(current))
+                       break;
+               if (polling_interval < 128)
+                       polling_interval *= 2;
+       }
+
+       return !!(parport_ip32_read_econtrol(p) & ECR_F_EMPTY);
+}
+
+/**
+ * parport_ip32_get_fifo_residue - reset FIFO
+ * @p:         pointer to &struct parport
+ * @mode:      current operation mode (ECR_MODE_PPF or ECR_MODE_ECP)
+ *
+ * This function resets FIFO, and returns the number of bytes remaining in it.
+ */
+static unsigned int parport_ip32_get_fifo_residue(struct parport *p,
+                                                 unsigned int mode)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       unsigned int residue;
+       unsigned int cnfga;
+
+       /* FIXME - We are missing one byte if the printer is off-line.  I
+        * don't know how to detect this.  It looks that the full bit is not
+        * always reliable.  For the moment, the problem is avoided in most
+        * cases by testing for BUSY in parport_ip32_compat_write_data().
+        */
+       if (parport_ip32_read_econtrol(p) & ECR_F_EMPTY)
+               residue = 0;
+       else {
+               pr_debug1(PPIP32 "%s: FIFO is stuck\n", p->name);
+
+               /* Stop all transfers.
+                *
+                * Microsoft's document instructs to drive DCR_STROBE to 0,
+                * but it doesn't work (at least in Compatibility mode, not
+                * tested in ECP mode).  Switching directly to Test mode (as
+                * in parport_pc) is not an option: it does confuse the port,
+                * ECP service interrupts are no more working after that.  A
+                * hard reset is then needed to revert to a sane state.
+                *
+                * Let's hope that the FIFO is really stuck and that the
+                * peripheral doesn't wake up now.
+                */
+               parport_ip32_frob_control(p, DCR_STROBE, 0);
+
+               /* Fill up FIFO */
+               for (residue = priv->fifo_depth; residue > 0; residue--) {
+                       if (parport_ip32_read_econtrol(p) & ECR_F_FULL)
+                               break;
+                       writeb(0x00, priv->regs.fifo);
+               }
+       }
+       if (residue)
+               pr_debug1(PPIP32 "%s: %d PWord%s left in FIFO\n",
+                         p->name, residue,
+                         (residue == 1) ? " was" : "s were");
+
+       /* Now reset the FIFO */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+
+       /* Host recovery for ECP mode */
+       if (mode == ECR_MODE_ECP) {
+               parport_ip32_data_reverse(p);
+               parport_ip32_frob_control(p, DCR_nINIT, 0);
+               if (parport_wait_peripheral(p, DSR_PERROR, 0))
+                       pr_debug1(PPIP32 "%s: PEerror timeout 1 in %s\n",
+                                 p->name, __func__);
+               parport_ip32_frob_control(p, DCR_STROBE, DCR_STROBE);
+               parport_ip32_frob_control(p, DCR_nINIT, DCR_nINIT);
+               if (parport_wait_peripheral(p, DSR_PERROR, DSR_PERROR))
+                       pr_debug1(PPIP32 "%s: PEerror timeout 2 in %s\n",
+                                 p->name, __func__);
+       }
+
+       /* Adjust residue if needed */
+       parport_ip32_set_mode(p, ECR_MODE_CFG);
+       cnfga = readb(priv->regs.cnfgA);
+       if (!(cnfga & CNFGA_nBYTEINTRANS)) {
+               pr_debug1(PPIP32 "%s: cnfgA contains 0x%02x\n",
+                         p->name, cnfga);
+               pr_debug1(PPIP32 "%s: Accounting for extra byte\n",
+                         p->name);
+               residue++;
+       }
+
+       /* Don't care about partial PWords since we do not support
+        * PWord != 1 byte. */
+
+       /* Back to forward PS2 mode. */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       parport_ip32_data_forward(p);
+
+       return residue;
+}
+
+/**
+ * parport_ip32_compat_write_data - write a block of data in SPP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ * @flags:     ignored
+ */
+static size_t parport_ip32_compat_write_data(struct parport *p,
+                                            const void *buf, size_t len,
+                                            int flags)
+{
+       static unsigned int ready_before = 1;
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport * const physport = p->physport;
+       size_t written = 0;
+
+       /* Special case: a timeout of zero means we cannot call schedule().
+        * Also if O_NONBLOCK is set then use the default implementation. */
+       if (physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
+               return parport_ieee1284_write_compat(p, buf, len, flags);
+
+       /* Reset FIFO, go in forward mode, and disable ackIntEn */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       parport_ip32_write_control(p, DCR_SELECT | DCR_nINIT);
+       parport_ip32_data_forward(p);
+       parport_ip32_disable_irq(p);
+       parport_ip32_set_mode(p, ECR_MODE_PPF);
+       physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+       /* Wait for peripheral to become ready */
+       if (parport_wait_peripheral(p, DSR_nBUSY | DSR_nFAULT,
+                                      DSR_nBUSY | DSR_nFAULT)) {
+               /* Avoid to flood the logs */
+               if (ready_before)
+                       printk(KERN_INFO PPIP32 "%s: not ready in %s\n",
+                              p->name, __func__);
+               ready_before = 0;
+               goto stop;
+       }
+       ready_before = 1;
+
+       written = parport_ip32_fifo_write_block(p, buf, len);
+
+       /* Wait FIFO to empty.  Timeout is proportional to FIFO_depth.  */
+       parport_ip32_drain_fifo(p, physport->cad->timeout * priv->fifo_depth);
+
+       /* Check for a potential residue */
+       written -= parport_ip32_get_fifo_residue(p, ECR_MODE_PPF);
+
+       /* Then, wait for BUSY to get low. */
+       if (parport_wait_peripheral(p, DSR_nBUSY, DSR_nBUSY))
+               printk(KERN_DEBUG PPIP32 "%s: BUSY timeout in %s\n",
+                      p->name, __func__);
+
+stop:
+       /* Reset FIFO */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+       return written;
+}
+
+/*
+ * FIXME - Insert here parport_ip32_ecp_read_data().
+ */
+
+/**
+ * parport_ip32_ecp_write_data - write a block of data in ECP mode
+ * @p:         pointer to &struct parport
+ * @buf:       buffer of data to write
+ * @len:       length of buffer @buf
+ * @flags:     ignored
+ */
+static size_t parport_ip32_ecp_write_data(struct parport *p,
+                                         const void *buf, size_t len,
+                                         int flags)
+{
+       static unsigned int ready_before = 1;
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport * const physport = p->physport;
+       size_t written = 0;
+
+       /* Special case: a timeout of zero means we cannot call schedule().
+        * Also if O_NONBLOCK is set then use the default implementation. */
+       if (physport->cad->timeout <= PARPORT_INACTIVITY_O_NONBLOCK)
+               return parport_ieee1284_ecp_write_data(p, buf, len, flags);
+
+       /* Negotiate to forward mode if necessary. */
+       if (physport->ieee1284.phase != IEEE1284_PH_FWD_IDLE) {
+               /* Event 47: Set nInit high. */
+               parport_ip32_frob_control(p, DCR_nINIT | DCR_AUTOFD,
+                                            DCR_nINIT | DCR_AUTOFD);
+
+               /* Event 49: PError goes high. */
+               if (parport_wait_peripheral(p, DSR_PERROR, DSR_PERROR)) {
+                       printk(KERN_DEBUG PPIP32 "%s: PError timeout in %s",
+                              p->name, __func__);
+                       physport->ieee1284.phase = IEEE1284_PH_ECP_DIR_UNKNOWN;
+                       return 0;
+               }
+       }
+
+       /* Reset FIFO, go in forward mode, and disable ackIntEn */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       parport_ip32_write_control(p, DCR_SELECT | DCR_nINIT);
+       parport_ip32_data_forward(p);
+       parport_ip32_disable_irq(p);
+       parport_ip32_set_mode(p, ECR_MODE_ECP);
+       physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
+
+       /* Wait for peripheral to become ready */
+       if (parport_wait_peripheral(p, DSR_nBUSY | DSR_nFAULT,
+                                      DSR_nBUSY | DSR_nFAULT)) {
+               /* Avoid to flood the logs */
+               if (ready_before)
+                       printk(KERN_INFO PPIP32 "%s: not ready in %s\n",
+                              p->name, __func__);
+               ready_before = 0;
+               goto stop;
+       }
+       ready_before = 1;
+
+       written = parport_ip32_fifo_write_block(p, buf, len);
+
+       /* Wait FIFO to empty.  Timeout is proportional to FIFO_depth.  */
+       parport_ip32_drain_fifo(p, physport->cad->timeout * priv->fifo_depth);
+
+       /* Check for a potential residue */
+       written -= parport_ip32_get_fifo_residue(p, ECR_MODE_ECP);
+
+       /* Then, wait for BUSY to get low. */
+       if (parport_wait_peripheral(p, DSR_nBUSY, DSR_nBUSY))
+               printk(KERN_DEBUG PPIP32 "%s: BUSY timeout in %s\n",
+                      p->name, __func__);
+
+stop:
+       /* Reset FIFO */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       physport->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
+       return written;
+}
+
+/*
+ * FIXME - Insert here parport_ip32_ecp_write_addr().
+ */
+
+/*--- Default parport operations ---------------------------------------*/
+
+static __initdata struct parport_operations parport_ip32_ops = {
+       .write_data             = parport_ip32_write_data,
+       .read_data              = parport_ip32_read_data,
+
+       .write_control          = parport_ip32_write_control,
+       .read_control           = parport_ip32_read_control,
+       .frob_control           = parport_ip32_frob_control,
+
+       .read_status            = parport_ip32_read_status,
+
+       .enable_irq             = parport_ip32_enable_irq,
+       .disable_irq            = parport_ip32_disable_irq,
+
+       .data_forward           = parport_ip32_data_forward,
+       .data_reverse           = parport_ip32_data_reverse,
+
+       .init_state             = parport_ip32_init_state,
+       .save_state             = parport_ip32_save_state,
+       .restore_state          = parport_ip32_restore_state,
+
+       .epp_write_data         = parport_ieee1284_epp_write_data,
+       .epp_read_data          = parport_ieee1284_epp_read_data,
+       .epp_write_addr         = parport_ieee1284_epp_write_addr,
+       .epp_read_addr          = parport_ieee1284_epp_read_addr,
+
+       .ecp_write_data         = parport_ieee1284_ecp_write_data,
+       .ecp_read_data          = parport_ieee1284_ecp_read_data,
+       .ecp_write_addr         = parport_ieee1284_ecp_write_addr,
+
+       .compat_write_data      = parport_ieee1284_write_compat,
+       .nibble_read_data       = parport_ieee1284_read_nibble,
+       .byte_read_data         = parport_ieee1284_read_byte,
+
+       .owner                  = THIS_MODULE,
+};
+
+/*--- Device detection -------------------------------------------------*/
+
+/**
+ * parport_ip32_ecp_supported - check for an ECP port
+ * @p:         pointer to the &parport structure
+ *
+ * Returns 1 if an ECP port is found, and 0 otherwise.  This function actually
+ * checks if an Extended Control Register seems to be present.  On successful
+ * return, the port is placed in SPP mode.
+ */
+static __init unsigned int parport_ip32_ecp_supported(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       unsigned int ecr;
+
+       ecr = ECR_MODE_PS2 | ECR_nERRINTR | ECR_SERVINTR;
+       writeb(ecr, priv->regs.ecr);
+       if (readb(priv->regs.ecr) != (ecr | ECR_F_EMPTY))
+               goto fail;
+
+       pr_probe(p, "Found working ECR register\n");
+       parport_ip32_set_mode(p, ECR_MODE_SPP);
+       parport_ip32_write_control(p, DCR_SELECT | DCR_nINIT);
+       return 1;
+
+fail:
+       pr_probe(p, "ECR register not found\n");
+       return 0;
+}
+
+/**
+ * parport_ip32_fifo_supported - check for FIFO parameters
+ * @p:         pointer to the &parport structure
+ *
+ * Check for FIFO parameters of an Extended Capabilities Port.  Returns 1 on
+ * success, and 0 otherwise.  Adjust FIFO parameters in the parport structure.
+ * On return, the port is placed in SPP mode.
+ */
+static __init unsigned int parport_ip32_fifo_supported(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       unsigned int configa, configb;
+       unsigned int pword;
+       unsigned int i;
+
+       /* Configuration mode */
+       parport_ip32_set_mode(p, ECR_MODE_CFG);
+       configa = readb(priv->regs.cnfgA);
+       configb = readb(priv->regs.cnfgB);
+
+       /* Find out PWord size */
+       switch (configa & CNFGA_ID_MASK) {
+       case CNFGA_ID_8:
+               pword = 1;
+               break;
+       case CNFGA_ID_16:
+               pword = 2;
+               break;
+       case CNFGA_ID_32:
+               pword = 4;
+               break;
+       default:
+               pr_probe(p, "Unknown implementation ID: 0x%0x\n",
+                        (configa & CNFGA_ID_MASK) >> CNFGA_ID_SHIFT);
+               goto fail;
+               break;
+       }
+       if (pword != 1) {
+               pr_probe(p, "Unsupported PWord size: %u\n", pword);
+               goto fail;
+       }
+       priv->pword = pword;
+       pr_probe(p, "PWord is %u bits\n", 8 * priv->pword);
+
+       /* Check for compression support */
+       writeb(configb | CNFGB_COMPRESS, priv->regs.cnfgB);
+       if (readb(priv->regs.cnfgB) & CNFGB_COMPRESS)
+               pr_probe(p, "Hardware compression detected (unsupported)\n");
+       writeb(configb & ~CNFGB_COMPRESS, priv->regs.cnfgB);
+
+       /* Reset FIFO and go in test mode (no interrupt, no DMA) */
+       parport_ip32_set_mode(p, ECR_MODE_TST);
+
+       /* FIFO must be empty now */
+       if (!(readb(priv->regs.ecr) & ECR_F_EMPTY)) {
+               pr_probe(p, "FIFO not reset\n");
+               goto fail;
+       }
+
+       /* Find out FIFO depth. */
+       priv->fifo_depth = 0;
+       for (i = 0; i < 1024; i++) {
+               if (readb(priv->regs.ecr) & ECR_F_FULL) {
+                       /* FIFO full */
+                       priv->fifo_depth = i;
+                       break;
+               }
+               writeb((u8)i, priv->regs.fifo);
+       }
+       if (i >= 1024) {
+               pr_probe(p, "Can't fill FIFO\n");
+               goto fail;
+       }
+       if (!priv->fifo_depth) {
+               pr_probe(p, "Can't get FIFO depth\n");
+               goto fail;
+       }
+       pr_probe(p, "FIFO is %u PWords deep\n", priv->fifo_depth);
+
+       /* Enable interrupts */
+       parport_ip32_frob_econtrol(p, ECR_SERVINTR, 0);
+
+       /* Find out writeIntrThreshold: number of PWords we know we can write
+        * if we get an interrupt. */
+       priv->writeIntrThreshold = 0;
+       for (i = 0; i < priv->fifo_depth; i++) {
+               if (readb(priv->regs.fifo) != (u8)i) {
+                       pr_probe(p, "Invalid data in FIFO\n");
+                       goto fail;
+               }
+               if (!priv->writeIntrThreshold
+                   && readb(priv->regs.ecr) & ECR_SERVINTR)
+                       /* writeIntrThreshold reached */
+                       priv->writeIntrThreshold = i + 1;
+               if (i + 1 < priv->fifo_depth
+                   && readb(priv->regs.ecr) & ECR_F_EMPTY) {
+                       /* FIFO empty before the last byte? */
+                       pr_probe(p, "Data lost in FIFO\n");
+                       goto fail;
+               }
+       }
+       if (!priv->writeIntrThreshold) {
+               pr_probe(p, "Can't get writeIntrThreshold\n");
+               goto fail;
+       }
+       pr_probe(p, "writeIntrThreshold is %u\n", priv->writeIntrThreshold);
+
+       /* FIFO must be empty now */
+       if (!(readb(priv->regs.ecr) & ECR_F_EMPTY)) {
+               pr_probe(p, "Can't empty FIFO\n");
+               goto fail;
+       }
+
+       /* Reset FIFO */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       /* Set reverse direction (must be in PS2 mode) */
+       parport_ip32_data_reverse(p);
+       /* Test FIFO, no interrupt, no DMA */
+       parport_ip32_set_mode(p, ECR_MODE_TST);
+       /* Enable interrupts */
+       parport_ip32_frob_econtrol(p, ECR_SERVINTR, 0);
+
+       /* Find out readIntrThreshold: number of PWords we can read if we get
+        * an interrupt. */
+       priv->readIntrThreshold = 0;
+       for (i = 0; i < priv->fifo_depth; i++) {
+               writeb(0xaa, priv->regs.fifo);
+               if (readb(priv->regs.ecr) & ECR_SERVINTR) {
+                       /* readIntrThreshold reached */
+                       priv->readIntrThreshold = i + 1;
+                       break;
+               }
+       }
+       if (!priv->readIntrThreshold) {
+               pr_probe(p, "Can't get readIntrThreshold\n");
+               goto fail;
+       }
+       pr_probe(p, "readIntrThreshold is %u\n", priv->readIntrThreshold);
+
+       /* Reset ECR */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       parport_ip32_data_forward(p);
+       parport_ip32_set_mode(p, ECR_MODE_SPP);
+       return 1;
+
+fail:
+       priv->fifo_depth = 0;
+       parport_ip32_set_mode(p, ECR_MODE_SPP);
+       return 0;
+}
+
+/*--- Initialization code ----------------------------------------------*/
+
+/**
+ * parport_ip32_make_isa_registers - compute (ISA) register addresses
+ * @regs:      pointer to &struct parport_ip32_regs to fill
+ * @base:      base address of standard and EPP registers
+ * @base_hi:   base address of ECP registers
+ * @regshift:  how much to shift register offset by
+ *
+ * Compute register addresses, according to the ISA standard.  The addresses
+ * of the standard and EPP registers are computed from address @base.  The
+ * addresses of the ECP registers are computed from address @base_hi.
+ */
+static void __init
+parport_ip32_make_isa_registers(struct parport_ip32_regs *regs,
+                               void __iomem *base, void __iomem *base_hi,
+                               unsigned int regshift)
+{
+#define r_base(offset)    ((u8 __iomem *)base    + ((offset) << regshift))
+#define r_base_hi(offset) ((u8 __iomem *)base_hi + ((offset) << regshift))
+       *regs = (struct parport_ip32_regs){
+               .data           = r_base(0),
+               .dsr            = r_base(1),
+               .dcr            = r_base(2),
+               .eppAddr        = r_base(3),
+               .eppData0       = r_base(4),
+               .eppData1       = r_base(5),
+               .eppData2       = r_base(6),
+               .eppData3       = r_base(7),
+               .ecpAFifo       = r_base(0),
+               .fifo           = r_base_hi(0),
+               .cnfgA          = r_base_hi(0),
+               .cnfgB          = r_base_hi(1),
+               .ecr            = r_base_hi(2)
+       };
+#undef r_base_hi
+#undef r_base
+}
+
+/**
+ * parport_ip32_probe_port - probe and register IP32 built-in parallel port
+ *
+ * Returns the new allocated &parport structure.  On error, an error code is
+ * encoded in return value with the ERR_PTR function.
+ */
+static __init struct parport *parport_ip32_probe_port(void)
+{
+       struct parport_ip32_regs regs;
+       struct parport_ip32_private *priv = NULL;
+       struct parport_operations *ops = NULL;
+       struct parport *p = NULL;
+       int err;
+
+       parport_ip32_make_isa_registers(&regs, &mace->isa.parallel,
+                                       &mace->isa.ecp1284, 8 /* regshift */);
+
+       ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL);
+       priv = kmalloc(sizeof(struct parport_ip32_private), GFP_KERNEL);
+       p = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, ops);
+       if (ops == NULL || priv == NULL || p == NULL) {
+               err = -ENOMEM;
+               goto fail;
+       }
+       p->base = MACE_BASE + offsetof(struct sgi_mace, isa.parallel);
+       p->base_hi = MACE_BASE + offsetof(struct sgi_mace, isa.ecp1284);
+       p->private_data = priv;
+
+       *ops = parport_ip32_ops;
+       *priv = (struct parport_ip32_private){
+               .regs                   = regs,
+               .dcr_writable           = DCR_DIR | DCR_SELECT | DCR_nINIT |
+                                         DCR_AUTOFD | DCR_STROBE,
+               .irq_mode               = PARPORT_IP32_IRQ_FWD,
+       };
+       init_completion(&priv->irq_complete);
+
+       /* Probe port. */
+       if (!parport_ip32_ecp_supported(p)) {
+               err = -ENODEV;
+               goto fail;
+       }
+       parport_ip32_dump_state(p, "begin init", 0);
+
+       /* We found what looks like a working ECR register.  Simply assume
+        * that all modes are correctly supported.  Enable basic modes. */
+       p->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT;
+       p->modes |= PARPORT_MODE_TRISTATE;
+
+       if (!parport_ip32_fifo_supported(p)) {
+               printk(KERN_WARNING PPIP32
+                      "%s: error: FIFO disabled\n", p->name);
+               /* Disable hardware modes depending on a working FIFO. */
+               features &= ~PARPORT_IP32_ENABLE_SPP;
+               features &= ~PARPORT_IP32_ENABLE_ECP;
+               /* DMA is not needed if FIFO is not supported.  */
+               features &= ~PARPORT_IP32_ENABLE_DMA;
+       }
+
+       /* Request IRQ */
+       if (features & PARPORT_IP32_ENABLE_IRQ) {
+               int irq = MACEISA_PARALLEL_IRQ;
+               if (request_irq(irq, parport_ip32_interrupt, 0, p->name, p)) {
+                       printk(KERN_WARNING PPIP32
+                              "%s: error: IRQ disabled\n", p->name);
+                       /* DMA cannot work without interrupts. */
+                       features &= ~PARPORT_IP32_ENABLE_DMA;
+               } else {
+                       pr_probe(p, "Interrupt support enabled\n");
+                       p->irq = irq;
+                       priv->dcr_writable |= DCR_IRQ;
+               }
+       }
+
+       /* Allocate DMA resources */
+       if (features & PARPORT_IP32_ENABLE_DMA) {
+               if (parport_ip32_dma_register())
+                       printk(KERN_WARNING PPIP32
+                              "%s: error: DMA disabled\n", p->name);
+               else {
+                       pr_probe(p, "DMA support enabled\n");
+                       p->dma = 0; /* arbitrary value != PARPORT_DMA_NONE */
+                       p->modes |= PARPORT_MODE_DMA;
+               }
+       }
+
+       if (features & PARPORT_IP32_ENABLE_SPP) {
+               /* Enable compatibility FIFO mode */
+               p->ops->compat_write_data = parport_ip32_compat_write_data;
+               p->modes |= PARPORT_MODE_COMPAT;
+               pr_probe(p, "Hardware support for SPP mode enabled\n");
+       }
+       if (features & PARPORT_IP32_ENABLE_EPP) {
+               /* Set up access functions to use EPP hardware. */
+               p->ops->epp_read_data = parport_ip32_epp_read_data;
+               p->ops->epp_write_data = parport_ip32_epp_write_data;
+               p->ops->epp_read_addr = parport_ip32_epp_read_addr;
+               p->ops->epp_write_addr = parport_ip32_epp_write_addr;
+               p->modes |= PARPORT_MODE_EPP;
+               pr_probe(p, "Hardware support for EPP mode enabled\n");
+       }
+       if (features & PARPORT_IP32_ENABLE_ECP) {
+               /* Enable ECP FIFO mode */
+               p->ops->ecp_write_data = parport_ip32_ecp_write_data;
+               /* FIXME - not implemented */
+/*             p->ops->ecp_read_data  = parport_ip32_ecp_read_data; */
+/*             p->ops->ecp_write_addr = parport_ip32_ecp_write_addr; */
+               p->modes |= PARPORT_MODE_ECP;
+               pr_probe(p, "Hardware support for ECP mode enabled\n");
+       }
+
+       /* Initialize the port with sensible values */
+       parport_ip32_set_mode(p, ECR_MODE_PS2);
+       parport_ip32_write_control(p, DCR_SELECT | DCR_nINIT);
+       parport_ip32_data_forward(p);
+       parport_ip32_disable_irq(p);
+       parport_ip32_write_data(p, 0x00);
+       parport_ip32_dump_state(p, "end init", 0);
+
+       /* Print out what we found */
+       printk(KERN_INFO "%s: SGI IP32 at 0x%lx (0x%lx)",
+              p->name, p->base, p->base_hi);
+       if (p->irq != PARPORT_IRQ_NONE)
+               printk(", irq %d", p->irq);
+       printk(" [");
+#define printmode(x)   if (p->modes & PARPORT_MODE_##x)                \
+                               printk("%s%s", f++ ? "," : "", #x)
+       {
+               unsigned int f = 0;
+               printmode(PCSPP);
+               printmode(TRISTATE);
+               printmode(COMPAT);
+               printmode(EPP);
+               printmode(ECP);
+               printmode(DMA);
+       }
+#undef printmode
+       printk("]\n");
+
+       parport_announce_port(p);
+       return p;
+
+fail:
+       if (p)
+               parport_put_port(p);
+       kfree(priv);
+       kfree(ops);
+       return ERR_PTR(err);
+}
+
+/**
+ * parport_ip32_unregister_port - unregister a parallel port
+ * @p:         pointer to the &struct parport
+ *
+ * Unregisters a parallel port and free previously allocated resources
+ * (memory, IRQ, ...).
+ */
+static __exit void parport_ip32_unregister_port(struct parport *p)
+{
+       struct parport_ip32_private * const priv = p->physport->private_data;
+       struct parport_operations *ops = p->ops;
+
+       parport_remove_port(p);
+       if (p->modes & PARPORT_MODE_DMA)
+               parport_ip32_dma_unregister();
+       if (p->irq != PARPORT_IRQ_NONE)
+               free_irq(p->irq, p);
+       parport_put_port(p);
+       kfree(priv);
+       kfree(ops);
+}
+
+/**
+ * parport_ip32_init - module initialization function
+ */
+static int __init parport_ip32_init(void)
+{
+       pr_info(PPIP32 "SGI IP32 built-in parallel port driver v0.6\n");
+       pr_debug1(PPIP32 "Compiled on %s, %s\n", __DATE__, __TIME__);
+       this_port = parport_ip32_probe_port();
+       return IS_ERR(this_port) ? PTR_ERR(this_port) : 0;
+}
+
+/**
+ * parport_ip32_exit - module termination function
+ */
+static void __exit parport_ip32_exit(void)
+{
+       parport_ip32_unregister_port(this_port);
+}
+
+/*--- Module stuff -----------------------------------------------------*/
+
+MODULE_AUTHOR("Arnaud Giersch <arnaud.giersch@free.fr>");
+MODULE_DESCRIPTION("SGI IP32 built-in parallel port driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.6");         /* update in parport_ip32_init() too */
+
+module_init(parport_ip32_init);
+module_exit(parport_ip32_exit);
+
+module_param(verbose_probing, bool, S_IRUGO);
+MODULE_PARM_DESC(verbose_probing, "Log chit-chat during initialization");
+
+module_param(features, uint, S_IRUGO);
+MODULE_PARM_DESC(features,
+                "Bit mask of features to enable"
+                ", bit 0: IRQ support"
+                ", bit 1: DMA support"
+                ", bit 2: hardware SPP mode"
+                ", bit 3: hardware EPP mode"
+                ", bit 4: hardware ECP mode");
+
+/*--- Inform (X)Emacs about preferred coding style ---------------------*/
+/*
+ * Local Variables:
+ * mode: c
+ * c-file-style: "linux"
+ * indent-tabs-mode: t
+ * tab-width: 8
+ * fill-column: 78
+ * ispell-local-dictionary: "american"
+ * End:
+ */
index 76dd077..166de35 100644 (file)
@@ -329,9 +329,9 @@ static int __devinit parport_register (struct pci_dev *dev,
 
                if (priv->num_par == ARRAY_SIZE (priv->port)) {
                        printk (KERN_WARNING
-                               "parport_serial: %s: only %u parallel ports "
+                               "parport_serial: %s: only %zu parallel ports "
                                "supported (%d reported)\n", pci_name (dev),
-                               ARRAY_SIZE (priv->port), card->numports);
+                               ARRAY_SIZE(priv->port), card->numports);
                        break;
                }
 
index b62aee8..ea83b70 100644 (file)
@@ -199,7 +199,7 @@ static ssize_t parport_read_device_id (struct parport *port, char *buffer,
 
                if (port->physport->ieee1284.phase != IEEE1284_PH_HBUSY_DAVAIL) {
                        if (belen != len) {
-                               printk (KERN_DEBUG "%s: Device ID was %d bytes"
+                               printk (KERN_DEBUG "%s: Device ID was %zd bytes"
                                        " while device told it would be %d"
                                        " bytes\n",
                                        port->name, len, belen);
@@ -214,7 +214,7 @@ static ssize_t parport_read_device_id (struct parport *port, char *buffer,
                if (buffer[len-1] == ';') {
                        printk (KERN_DEBUG "%s: Device ID reading stopped"
                                " before device told data not available. "
-                               "Current idlen %d of %d, len bytes %02X %02X\n",
+                               "Current idlen %u of %u, len bytes %02X %02X\n",
                                port->name, current_idlen, numidlens,
                                length[0], length[1]);
                        goto done;
index 6f50cc9..6912399 100644 (file)
@@ -55,13 +55,21 @@ config DASD_DIAG
          Disks under VM.  If you are not running under VM or unsure what it is,
          say "N".
 
+config DASD_EER
+       tristate "Extended error reporting (EER)"
+       depends on DASD
+       help
+         This driver provides a character device interface to the
+          DASD extended error reporting. This is only needed if you want to
+          use applications written for the EER facility.
+
 config DASD_CMB
        tristate "Compatibility interface for DASD channel measurement blocks"
        depends on DASD
        help
-         This driver provides an additional interface to the channel measurement
-         facility, which is normally accessed though sysfs, with a set of
-         ioctl functions specific to the dasd driver.
+         This driver provides an additional interface to the channel
+          measurement facility, which is normally accessed though sysfs, with
+          a set of ioctl functions specific to the dasd driver.
          This is only needed if you want to use applications written for
          linux-2.4 dasd channel measurement facility interface.
 
index 58c6780..0c0d871 100644 (file)
@@ -5,6 +5,7 @@
 dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o
 dasd_fba_mod-objs  := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o
 dasd_diag_mod-objs := dasd_diag.o
+dasd_eer_mod-objs := dasd_eer.o
 dasd_mod-objs      := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
                        dasd_genhd.o dasd_erp.o
 
@@ -13,5 +14,6 @@ obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
 obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
 obj-$(CONFIG_DASD_FBA)  += dasd_fba_mod.o
 obj-$(CONFIG_DASD_CMB)  += dasd_cmb.o
+obj-$(CONFIG_DASD_EER)  += dasd_eer.o
 obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
 obj-$(CONFIG_DCSSBLK) += dcssblk.o
index abdf1ee..08c88fc 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/buffer_head.h>
 #include <linux/hdreg.h>
+#include <linux/notifier.h>
 
 #include <asm/ccwdev.h>
 #include <asm/ebcdic.h>
@@ -57,6 +58,7 @@ static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);
 static void dasd_flush_ccw_queue(struct dasd_device *, int);
 static void dasd_tasklet(struct dasd_device *);
 static void do_kick_device(void *data);
+static void dasd_disable_eer(struct dasd_device *device);
 
 /*
  * SECTION: Operations on the device structure.
@@ -151,6 +153,8 @@ dasd_state_new_to_known(struct dasd_device *device)
 static inline void
 dasd_state_known_to_new(struct dasd_device * device)
 {
+       /* disable extended error reporting for this device */
+       dasd_disable_eer(device);
        /* Forget the discipline information. */
        device->discipline = NULL;
        device->state = DASD_STATE_NEW;
@@ -867,6 +871,9 @@ dasd_handle_state_change_pending(struct dasd_device *device)
        struct dasd_ccw_req *cqr;
        struct list_head *l, *n;
 
+       /* first of all call extended error reporting */
+       dasd_write_eer_trigger(DASD_EER_STATECHANGE, device, NULL);
+
        device->stopped &= ~DASD_STOPPED_PENDING;
 
         /* restart all 'running' IO on queue */
@@ -1086,6 +1093,19 @@ restart:
                        }
                        goto restart;
                }
+
+               /* first of all call extended error reporting */
+               if (device->eer && cqr->status == DASD_CQR_FAILED) {
+                       dasd_write_eer_trigger(DASD_EER_FATALERROR,
+                                              device, cqr);
+
+                       /* restart request  */
+                       cqr->status = DASD_CQR_QUEUED;
+                       cqr->retries = 255;
+                       device->stopped |= DASD_STOPPED_QUIESCE;
+                       goto restart;
+               }
+
                /* Process finished ERP request. */
                if (cqr->refers) {
                        __dasd_process_erp(device, cqr);
@@ -1223,7 +1243,8 @@ __dasd_start_head(struct dasd_device * device)
        cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
         /* check FAILFAST */
        if (device->stopped & ~DASD_STOPPED_PENDING &&
-           test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags)) {
+           test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+           (!device->eer)) {
                cqr->status = DASD_CQR_FAILED;
                dasd_schedule_bh(device);
        }
@@ -1965,6 +1986,9 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
        switch (event) {
        case CIO_GONE:
        case CIO_NO_PATH:
+               /* first of all call extended error reporting */
+               dasd_write_eer_trigger(DASD_EER_NOPATH, device, NULL);
+
                if (device->state < DASD_STATE_BASIC)
                        break;
                /* Device is active. We want to keep it. */
@@ -2022,6 +2046,51 @@ dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
        put_driver(drv);
 }
 
+/*
+ * notifications for extended error reports
+ */
+static struct notifier_block *dasd_eer_chain;
+
+int
+dasd_register_eer_notifier(struct notifier_block *nb)
+{
+       return notifier_chain_register(&dasd_eer_chain, nb);
+}
+
+int
+dasd_unregister_eer_notifier(struct notifier_block *nb)
+{
+       return notifier_chain_unregister(&dasd_eer_chain, nb);
+}
+
+/*
+ * Notify the registered error reporting module of a problem
+ */
+void
+dasd_write_eer_trigger(unsigned int id, struct dasd_device *device,
+                      struct dasd_ccw_req *cqr)
+{
+       if (device->eer) {
+               struct dasd_eer_trigger temp;
+               temp.id = id;
+               temp.device = device;
+               temp.cqr = cqr;
+               notifier_call_chain(&dasd_eer_chain, DASD_EER_TRIGGER,
+                                   (void *)&temp);
+       }
+}
+
+/*
+ * Tell the registered error reporting module to disable error reporting for
+ * a given device and to cleanup any private data structures on that device.
+ */
+static void
+dasd_disable_eer(struct dasd_device *device)
+{
+       notifier_call_chain(&dasd_eer_chain, DASD_EER_DISABLE, (void *)device);
+}
+
+
 static int __init
 dasd_init(void)
 {
@@ -2103,6 +2172,11 @@ EXPORT_SYMBOL_GPL(dasd_generic_set_online);
 EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
 EXPORT_SYMBOL_GPL(dasd_generic_auto_online);
 
+EXPORT_SYMBOL(dasd_register_eer_notifier);
+EXPORT_SYMBOL(dasd_unregister_eer_notifier);
+EXPORT_SYMBOL(dasd_write_eer_trigger);
+
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index 4ee0f93..c811380 100644 (file)
@@ -1108,6 +1108,9 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
                case 0x0B:
                        DEV_MESSAGE(KERN_WARNING, device, "%s",
                                    "FORMAT F - Volume is suspended duplex");
+                       /* call extended error reporting (EER) */
+                       dasd_write_eer_trigger(DASD_EER_PPRCSUSPEND, device,
+                                              erp->refers);
                        break;
                case 0x0C:
                        DEV_MESSAGE(KERN_WARNING, device, "%s",
index bc3823d..e15dd79 100644 (file)
@@ -29,6 +29,7 @@
 #define DASD_ECKD_CCW_PSF               0x27
 #define DASD_ECKD_CCW_RSSD              0x3e
 #define DASD_ECKD_CCW_LOCATE_RECORD     0x47
+#define DASD_ECKD_CCW_SNSS               0x54
 #define DASD_ECKD_CCW_DEFINE_EXTENT     0x63
 #define DASD_ECKD_CCW_WRITE_MT          0x85
 #define DASD_ECKD_CCW_READ_MT           0x86
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
new file mode 100644 (file)
index 0000000..f70cd77
--- /dev/null
@@ -0,0 +1,1090 @@
+/*
+ *     character device driver for extended error reporting
+ *
+ *
+ *     Copyright (C) 2005 IBM Corporation
+ *     extended error reporting for DASD ECKD devices
+ *     Author(s): Stefan Weinhuber <wein@de.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/notifier.h>
+
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+#include <asm/ebcdic.h>
+
+#include "dasd_int.h"
+#include "dasd_eckd.h"
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("Stefan Weinhuber <wein@de.ibm.com>");
+MODULE_DESCRIPTION("DASD extended error reporting module");
+
+
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
+#endif                         /* PRINTK_HEADER */
+#define PRINTK_HEADER "dasd(eer):"
+
+
+
+
+
+/*****************************************************************************/
+/*      the internal buffer                                                  */
+/*****************************************************************************/
+
+/*
+ * The internal buffer is meant to store obaque blobs of data, so it doesn't
+ * know of higher level concepts like triggers.
+ * It consists of a number of pages that are used as a ringbuffer. Each data
+ * blob is stored in a simple record that consists of an integer, which
+ * contains the size of the following data, and the data bytes themselfes.
+ *
+ * To allow for multiple independent readers we create one internal buffer
+ * each time the device is opened and destroy the buffer when the file is
+ * closed again.
+ *
+ * One record can be written to a buffer by using the functions
+ * - dasd_eer_start_record (one time per record to write the size to the buffer
+ *                          and reserve the space for the data)
+ * - dasd_eer_write_buffer (one or more times per record to write the data)
+ * The data can be written in several steps but you will have to compute
+ * the total size up front for the invocation of dasd_eer_start_record.
+ * If the ringbuffer is full, dasd_eer_start_record will remove the required
+ * number of old records.
+ *
+ * A record is typically read in two steps, first read the integer that
+ * specifies the size of the following data, then read the data.
+ * Both can be done by
+ * - dasd_eer_read_buffer
+ *
+ * For all mentioned functions you need to get the bufferlock first and keep it
+ * until a complete record is written or read.
+ */
+
+
+/*
+ * Alle information necessary to keep track of an internal buffer is kept in
+ * a struct eerbuffer. The buffer specific to a file pointer is strored in
+ * the private_data field of that file. To be able to write data to all
+ * existing buffers, each buffer is also added to the bufferlist.
+ * If the user doesn't want to read a complete record in one go, we have to
+ * keep track of the rest of the record. residual stores the number of bytes
+ * that are still to deliver. If the rest of the record is invalidated between
+ * two reads then residual will be set to -1 so that the next read will fail.
+ * All entries in the eerbuffer structure are protected with the bufferlock.
+ * To avoid races between writing to a buffer on the one side and creating
+ * and destroying buffers on the other side, the bufferlock must also be used
+ * to protect the bufferlist.
+ */
+
+struct eerbuffer {
+       struct list_head list;
+       char **buffer;
+       int buffersize;
+       int buffer_page_count;
+       int head;
+        int tail;
+       int residual;
+};
+
+LIST_HEAD(bufferlist);
+
+static spinlock_t bufferlock = SPIN_LOCK_UNLOCKED;
+
+DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue);
+
+/*
+ * How many free bytes are available on the buffer.
+ * needs to be called with bufferlock held
+ */
+static int
+dasd_eer_get_free_bytes(struct eerbuffer *eerb)
+{
+       if (eerb->head < eerb->tail) {
+               return eerb->tail - eerb->head - 1;
+       } else
+               return eerb->buffersize - eerb->head + eerb->tail -1;
+}
+
+/*
+ * How many bytes of buffer space are used.
+ * needs to be called with bufferlock held
+ */
+static int
+dasd_eer_get_filled_bytes(struct eerbuffer *eerb)
+{
+
+       if (eerb->head >= eerb->tail) {
+               return eerb->head - eerb->tail;
+       } else
+               return eerb->buffersize - eerb->tail + eerb->head;
+}
+
+/*
+ * The dasd_eer_write_buffer function just copies count bytes of data
+ * to the buffer. Make sure to call dasd_eer_start_record first, to
+ * make sure that enough free space is available.
+ * needs to be called with bufferlock held
+ */
+static void
+dasd_eer_write_buffer(struct eerbuffer *eerb, int count, char *data)
+{
+
+       unsigned long headindex,localhead;
+       unsigned long rest, len;
+       char *nextdata;
+
+       nextdata = data;
+       rest = count;
+       while (rest > 0) {
+               headindex = eerb->head / PAGE_SIZE;
+               localhead = eerb->head % PAGE_SIZE;
+               len = min(rest, (PAGE_SIZE - localhead));
+               memcpy(eerb->buffer[headindex]+localhead, nextdata, len);
+               nextdata += len;
+               rest -= len;
+               eerb->head += len;
+               if ( eerb->head == eerb->buffersize )
+                       eerb->head = 0; /* wrap around */
+               if (eerb->head > eerb->buffersize) {
+                       MESSAGE(KERN_ERR, "%s", "runaway buffer head.");
+                       BUG();
+               }
+       }
+}
+
+/*
+ * needs to be called with bufferlock held
+ */
+static int
+dasd_eer_read_buffer(struct eerbuffer *eerb, int count, char *data)
+{
+
+       unsigned long tailindex,localtail;
+       unsigned long rest, len, finalcount;
+       char *nextdata;
+
+       finalcount = min(count, dasd_eer_get_filled_bytes(eerb));
+       nextdata = data;
+       rest = finalcount;
+       while (rest > 0) {
+               tailindex = eerb->tail / PAGE_SIZE;
+               localtail = eerb->tail % PAGE_SIZE;
+               len = min(rest, (PAGE_SIZE - localtail));
+               memcpy(nextdata, eerb->buffer[tailindex]+localtail, len);
+               nextdata += len;
+               rest -= len;
+               eerb->tail += len;
+               if ( eerb->tail == eerb->buffersize )
+                       eerb->tail = 0; /* wrap around */
+               if (eerb->tail > eerb->buffersize) {
+                       MESSAGE(KERN_ERR, "%s", "runaway buffer tail.");
+                       BUG();
+               }
+       }
+       return finalcount;
+}
+
+/*
+ * Whenever you want to write a blob of data to the internal buffer you
+ * have to start by using this function first. It will write the number
+ * of bytes that will be written to the buffer. If necessary it will remove
+ * old records to make room for the new one.
+ * needs to be called with bufferlock held
+ */
+static int
+dasd_eer_start_record(struct eerbuffer *eerb, int count)
+{
+       int tailcount;
+       if (count + sizeof(count) > eerb->buffersize)
+               return -ENOMEM;
+       while (dasd_eer_get_free_bytes(eerb) < count + sizeof(count)) {
+               if (eerb->residual > 0) {
+                       eerb->tail += eerb->residual;
+                       if (eerb->tail >= eerb->buffersize)
+                               eerb->tail -= eerb->buffersize;
+                       eerb->residual = -1;
+               }
+               dasd_eer_read_buffer(eerb, sizeof(tailcount),
+                                    (char*)(&tailcount));
+               eerb->tail += tailcount;
+               if (eerb->tail >= eerb->buffersize)
+                       eerb->tail -= eerb->buffersize;
+       }
+       dasd_eer_write_buffer(eerb, sizeof(count), (char*)(&count));
+
+       return 0;
+};
+
+/*
+ * release pages that are not used anymore
+ */
+static void
+dasd_eer_free_buffer_pages(char **buf, int no_pages)
+{
+       int i;
+
+       for (i = 0; i < no_pages; ++i) {
+               free_page((unsigned long)buf[i]);
+       }
+}
+
+/*
+ * allocate a new set of memory pages
+ */
+static int
+dasd_eer_allocate_buffer_pages(char **buf, int no_pages)
+{
+       int i;
+
+       for (i = 0; i < no_pages; ++i) {
+               buf[i] = (char *) get_zeroed_page(GFP_KERNEL);
+               if (!buf[i]) {
+                       dasd_eer_free_buffer_pages(buf, i);
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+/*
+ * empty the buffer by resetting head and tail
+ * In case there is a half read data blob in the buffer, we set residual
+ * to -1 to indicate that the remainder of the blob is lost.
+ */
+static void
+dasd_eer_purge_buffer(struct eerbuffer *eerb)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&bufferlock, flags);
+       if (eerb->residual > 0)
+               eerb->residual = -1;
+       eerb->tail=0;
+       eerb->head=0;
+       spin_unlock_irqrestore(&bufferlock, flags);
+}
+
+/*
+ * set the size of the buffer, newsize is the new number of pages to be used
+ * we don't try to copy any data back an forth, so any resize will also purge
+ * the buffer
+ */
+static int
+dasd_eer_resize_buffer(struct eerbuffer *eerb, int newsize)
+{
+       int i, oldcount, reuse;
+       char **new;
+       char **old;
+       unsigned long flags;
+
+       if (newsize < 1)
+               return -EINVAL;
+       if (eerb->buffer_page_count == newsize) {
+               /* documented behaviour is that any successfull invocation
+                 * will purge all records */
+               dasd_eer_purge_buffer(eerb);
+               return 0;
+       }
+       new = kmalloc(newsize*sizeof(char*), GFP_KERNEL);
+       if (!new)
+               return -ENOMEM;
+
+       reuse=min(eerb->buffer_page_count, newsize);
+       for (i = 0; i < reuse; ++i) {
+               new[i] = eerb->buffer[i];
+       }
+       if (eerb->buffer_page_count < newsize) {
+               if (dasd_eer_allocate_buffer_pages(
+                           &new[eerb->buffer_page_count],
+                           newsize - eerb->buffer_page_count)) {
+                       kfree(new);
+                       return -ENOMEM;
+               }
+       }
+
+       spin_lock_irqsave(&bufferlock, flags);
+       old = eerb->buffer;
+       eerb->buffer = new;
+       if (eerb->residual > 0)
+               eerb->residual = -1;
+       eerb->tail = 0;
+       eerb->head = 0;
+       oldcount = eerb->buffer_page_count;
+       eerb->buffer_page_count = newsize;
+       spin_unlock_irqrestore(&bufferlock, flags);
+
+       if (oldcount > newsize) {
+               for (i = newsize; i < oldcount; ++i) {
+                       free_page((unsigned long)old[i]);
+               }
+       }
+       kfree(old);
+
+       return 0;
+}
+
+
+/*****************************************************************************/
+/*      The extended error reporting functionality                           */
+/*****************************************************************************/
+
+/*
+ * When a DASD device driver wants to report an error, it calls the
+ * function dasd_eer_write_trigger (via a notifier mechanism) and gives the
+ * respective trigger ID as parameter.
+ * Currently there are four kinds of triggers:
+ *
+ * DASD_EER_FATALERROR:  all kinds of unrecoverable I/O problems
+ * DASD_EER_PPRCSUSPEND: PPRC was suspended
+ * DASD_EER_NOPATH:      There is no path to the device left.
+ * DASD_EER_STATECHANGE: The state of the device has changed.
+ *
+ * For the first three triggers all required information can be supplied by
+ * the caller. For these triggers a record is written by the function
+ * dasd_eer_write_standard_trigger.
+ *
+ * When dasd_eer_write_trigger is called to write a DASD_EER_STATECHANGE
+ * trigger, we have to gather the necessary sense data first. We cannot queue
+ * the necessary SNSS (sense subsystem status) request immediatly, since we
+ * are likely to run in a deadlock situation. Instead, we schedule a
+ * work_struct that calls the function dasd_eer_sense_subsystem_status to
+ * create and start an SNSS  request asynchronously.
+ *
+ * To avoid memory allocations at runtime, the necessary memory is allocated
+ * when the extended error reporting is enabled for a device (by
+ * dasd_eer_probe). There is one private eer data structure for each eer
+ * enabled DASD device. It contains memory for the work_struct, one SNSS cqr
+ * and a flags field that is used to coordinate the use of the cqr. The call
+ * to write a state change trigger can come in at any time, so we have one flag
+ * CQR_IN_USE that protects the cqr itself. When this flag indicates that the
+ * cqr is currently in use, dasd_eer_sense_subsystem_status cannot start a
+ * second request but sets the SNSS_REQUESTED flag instead.
+ *
+ * When the request is finished, the callback function dasd_eer_SNSS_cb
+ * is called. This function will invoke the function
+ * dasd_eer_write_SNSS_trigger to finally write the trigger. It will also
+ * check the SNSS_REQUESTED flag and if it is set it will call
+ * dasd_eer_sense_subsystem_status again.
+ *
+ * To avoid race conditions during the handling of the lock, the flags must
+ * be protected by the snsslock.
+ */
+
+struct dasd_eer_private {
+       struct dasd_ccw_req *cqr;
+       unsigned long flags;
+       struct work_struct worker;
+};
+
+static void dasd_eer_destroy(struct dasd_device *device,
+                            struct dasd_eer_private *eer);
+static int
+dasd_eer_write_trigger(struct dasd_eer_trigger *trigger);
+static void dasd_eer_sense_subsystem_status(void *data);
+static int dasd_eer_notify(struct notifier_block *self,
+                          unsigned long action, void *data);
+
+struct workqueue_struct *dasd_eer_workqueue;
+
+#define SNSS_DATA_SIZE 44
+static spinlock_t snsslock = SPIN_LOCK_UNLOCKED;
+
+#define DASD_EER_BUSID_SIZE 10
+struct dasd_eer_header {
+       __u32 total_size;
+       __u32 trigger;
+       __u64 tv_sec;
+       __u64 tv_usec;
+       char busid[DASD_EER_BUSID_SIZE];
+} __attribute__ ((packed));
+
+static struct notifier_block dasd_eer_nb = {
+       .notifier_call = dasd_eer_notify,
+};
+
+/*
+ * flags for use with dasd_eer_private
+ */
+#define CQR_IN_USE     0
+#define SNSS_REQUESTED 1
+
+/*
+ * This function checks if extended error reporting is available for a given
+ * dasd_device. If yes, then it creates and returns a struct dasd_eer,
+ * otherwise it returns an -EPERM error pointer.
+ */
+struct dasd_eer_private *
+dasd_eer_probe(struct dasd_device *device)
+{
+       struct dasd_eer_private *private;
+
+       if (!(device && device->discipline
+             && !strcmp(device->discipline->name, "ECKD"))) {
+               return ERR_PTR(-EPERM);
+       }
+       /* allocate the private data structure */
+       private = (struct dasd_eer_private *)kmalloc(
+               sizeof(struct dasd_eer_private), GFP_KERNEL);
+       if (!private) {
+               return ERR_PTR(-ENOMEM);
+       }
+       INIT_WORK(&private->worker, dasd_eer_sense_subsystem_status,
+                 (void *)device);
+       private->cqr = dasd_kmalloc_request("ECKD",
+                                           1 /* SNSS */ ,
+                                           SNSS_DATA_SIZE ,
+                                           device);
+       if (!private->cqr) {
+               kfree(private);
+               return ERR_PTR(-ENOMEM);
+       }
+       private->flags = 0;
+       return private;
+};
+
+/*
+ * If our private SNSS request is queued, remove it from the
+ * dasd ccw queue so we can free the requests memory.
+ */
+static void
+dasd_eer_dequeue_SNSS_request(struct dasd_device *device,
+                             struct dasd_eer_private *eer)
+{
+       struct list_head *lst, *nxt;
+       struct dasd_ccw_req *cqr, *erpcqr;
+       dasd_erp_fn_t erp_fn;
+
+       spin_lock_irq(get_ccwdev_lock(device->cdev));
+       list_for_each_safe(lst, nxt, &device->ccw_queue) {
+               cqr = list_entry(lst, struct dasd_ccw_req, list);
+               /* we are looking for two kinds or requests */
+               /* first kind: our SNSS request: */
+               if (cqr == eer->cqr) {
+                       if (cqr->status == DASD_CQR_IN_IO)
+                               device->discipline->term_IO(cqr);
+                       list_del(&cqr->list);
+                       break;
+               }
+               /* second kind: ERP requests for our SNSS request */
+               if (cqr->refers) {
+                       /* If this erp request chain ends in our cqr, then */
+                        /* cal the erp_postaction to clean it up  */
+                       erpcqr = cqr;
+                       while (erpcqr->refers) {
+                               erpcqr = erpcqr->refers;
+                       }
+                       if (erpcqr == eer->cqr) {
+                               erp_fn = device->discipline->erp_postaction(
+                                        cqr);
+                               erp_fn(cqr);
+                       }
+                       continue;
+               }
+       }
+       spin_unlock_irq(get_ccwdev_lock(device->cdev));
+}
+
+/*
+ * This function dismantles a struct dasd_eer that was created by
+ * dasd_eer_probe. Since we want to free our private data structure,
+ * we must make sure that the memory is not in use anymore.
+ * We have to flush the work queue and remove a possible SNSS request
+ * from the dasd queue.
+ */
+static void
+dasd_eer_destroy(struct dasd_device *device, struct dasd_eer_private *eer)
+{
+       flush_workqueue(dasd_eer_workqueue);
+       dasd_eer_dequeue_SNSS_request(device, eer);
+       dasd_kfree_request(eer->cqr, device);
+       kfree(eer);
+};
+
+/*
+ * enable the extended error reporting for a particular device
+ */
+static int
+dasd_eer_enable_on_device(struct dasd_device *device)
+{
+       void *eer;
+       if (!device)
+               return -ENODEV;
+       if (device->eer)
+               return 0;
+       if (!try_module_get(THIS_MODULE)) {
+               return -EINVAL;
+       }
+       eer = (void *)dasd_eer_probe(device);
+       if (IS_ERR(eer)) {
+               module_put(THIS_MODULE);
+               return PTR_ERR(eer);
+       }
+       device->eer = eer;
+       return 0;
+}
+
+/*
+ * enable the extended error reporting for a particular device
+ */
+static int
+dasd_eer_disable_on_device(struct dasd_device *device)
+{
+       struct dasd_eer_private *eer = device->eer;
+
+       if (!device)
+               return -ENODEV;
+       if (!device->eer)
+               return 0;
+       device->eer = NULL;
+       dasd_eer_destroy(device,eer);
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+/*
+ * Set extended error reporting (eer)
+ * Note: This will be registered as a DASD ioctl, to be called on DASD devices.
+ */
+static int
+dasd_ioctl_set_eer(struct block_device *bdev, int no, long args)
+{
+       struct dasd_device *device;
+       int intval;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+       if (bdev != bdev->bd_contains)
+               /* Error-reporting is not allowed for partitions */
+               return -EINVAL;
+       if (get_user(intval, (int __user *) args))
+               return -EFAULT;
+       device =  bdev->bd_disk->private_data;
+       if (device == NULL)
+               return -ENODEV;
+
+       intval = (intval != 0);
+       DEV_MESSAGE (KERN_DEBUG, device,
+                    "set eer on device to %d", intval);
+       if (intval)
+               return dasd_eer_enable_on_device(device);
+       else
+               return dasd_eer_disable_on_device(device);
+}
+
+/*
+ * Get value of extended error reporting.
+ * Note: This will be registered as a DASD ioctl, to be called on DASD devices.
+ */
+static int
+dasd_ioctl_get_eer(struct block_device *bdev, int no, long args)
+{
+       struct dasd_device *device;
+
+       device =  bdev->bd_disk->private_data;
+       if (device == NULL)
+               return -ENODEV;
+       return put_user((device->eer != NULL), (int __user *) args);
+}
+
+/*
+ * The following function can be used for those triggers that have
+ * all necessary data available when the function is called.
+ * If the parameter cqr is not NULL, the chain of requests will be searched
+ * for valid sense data, and all valid sense data sets will be added to
+ * the triggers data.
+ */
+static int
+dasd_eer_write_standard_trigger(int trigger, struct dasd_device *device,
+                               struct dasd_ccw_req *cqr)
+{
+       struct dasd_ccw_req *temp_cqr;
+       int data_size;
+       struct timeval tv;
+       struct dasd_eer_header header;
+       unsigned long flags;
+       struct eerbuffer *eerb;
+
+       /* go through cqr chain and count the valid sense data sets */
+       temp_cqr = cqr;
+       data_size = 0;
+       while (temp_cqr) {
+               if (temp_cqr->irb.esw.esw0.erw.cons)
+                       data_size += 32;
+               temp_cqr = temp_cqr->refers;
+       }
+
+       header.total_size = sizeof(header) + data_size + 4; /* "EOR" */
+       header.trigger = trigger;
+       do_gettimeofday(&tv);
+       header.tv_sec = tv.tv_sec;
+       header.tv_usec = tv.tv_usec;
+       strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+
+       spin_lock_irqsave(&bufferlock, flags);
+       list_for_each_entry(eerb, &bufferlist, list) {
+               dasd_eer_start_record(eerb, header.total_size);
+               dasd_eer_write_buffer(eerb, sizeof(header), (char*)(&header));
+               temp_cqr = cqr;
+               while (temp_cqr) {
+                       if (temp_cqr->irb.esw.esw0.erw.cons)
+                               dasd_eer_write_buffer(eerb, 32, cqr->irb.ecw);
+                       temp_cqr = temp_cqr->refers;
+               }
+               dasd_eer_write_buffer(eerb, 4,"EOR");
+       }
+       spin_unlock_irqrestore(&bufferlock, flags);
+
+       wake_up_interruptible(&dasd_eer_read_wait_queue);
+
+       return 0;
+}
+
+/*
+ * This function writes a DASD_EER_STATECHANGE trigger.
+ */
+static void
+dasd_eer_write_SNSS_trigger(struct dasd_device *device,
+                           struct dasd_ccw_req *cqr)
+{
+       int data_size;
+       int snss_rc;
+       struct timeval tv;
+       struct dasd_eer_header header;
+       unsigned long flags;
+       struct eerbuffer *eerb;
+
+       snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+       if (snss_rc)
+               data_size = 0;
+       else
+               data_size = SNSS_DATA_SIZE;
+
+       header.total_size = sizeof(header) + data_size + 4; /* "EOR" */
+       header.trigger = DASD_EER_STATECHANGE;
+       do_gettimeofday(&tv);
+       header.tv_sec = tv.tv_sec;
+       header.tv_usec = tv.tv_usec;
+       strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+
+       spin_lock_irqsave(&bufferlock, flags);
+       list_for_each_entry(eerb, &bufferlist, list) {
+               dasd_eer_start_record(eerb, header.total_size);
+               dasd_eer_write_buffer(eerb, sizeof(header),(char*)(&header));
+               if (!snss_rc)
+                       dasd_eer_write_buffer(eerb, SNSS_DATA_SIZE, cqr->data);
+               dasd_eer_write_buffer(eerb, 4,"EOR");
+       }
+       spin_unlock_irqrestore(&bufferlock, flags);
+
+       wake_up_interruptible(&dasd_eer_read_wait_queue);
+}
+
+/*
+ * callback function for use with SNSS request
+ */
+static void
+dasd_eer_SNSS_cb(struct dasd_ccw_req *cqr, void *data)
+{
+        struct dasd_device *device;
+       struct dasd_eer_private *private;
+       unsigned long irqflags;
+
+        device = (struct dasd_device *)data;
+       private = (struct dasd_eer_private *)device->eer;
+       dasd_eer_write_SNSS_trigger(device, cqr);
+       spin_lock_irqsave(&snsslock, irqflags);
+       if(!test_and_clear_bit(SNSS_REQUESTED, &private->flags)) {
+               clear_bit(CQR_IN_USE, &private->flags);
+               spin_unlock_irqrestore(&snsslock, irqflags);
+               return;
+       };
+       clear_bit(CQR_IN_USE, &private->flags);
+       spin_unlock_irqrestore(&snsslock, irqflags);
+       dasd_eer_sense_subsystem_status(device);
+       return;
+}
+
+/*
+ * clean a used cqr before using it again
+ */
+static void
+dasd_eer_clean_SNSS_request(struct dasd_ccw_req *cqr)
+{
+       struct ccw1 *cpaddr = cqr->cpaddr;
+       void *data = cqr->data;
+
+       memset(cqr, 0, sizeof(struct dasd_ccw_req));
+       memset(cpaddr, 0, sizeof(struct ccw1));
+       memset(data, 0, SNSS_DATA_SIZE);
+       cqr->cpaddr = cpaddr;
+       cqr->data = data;
+       strncpy((char *) &cqr->magic, "ECKD", 4);
+       ASCEBC((char *) &cqr->magic, 4);
+       set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+}
+
+/*
+ * build and start an SNSS request
+ * This function is called from a work queue so we have to
+ * pass the dasd_device pointer as a void pointer.
+ */
+static void
+dasd_eer_sense_subsystem_status(void *data)
+{
+       struct dasd_device *device;
+       struct dasd_eer_private *private;
+       struct dasd_ccw_req *cqr;
+       struct ccw1 *ccw;
+       unsigned long irqflags;
+
+       device = (struct dasd_device *)data;
+       private = (struct dasd_eer_private *)device->eer;
+       if (!private) /* device not eer enabled any more */
+               return;
+       cqr = private->cqr;
+       spin_lock_irqsave(&snsslock, irqflags);
+       if(test_and_set_bit(CQR_IN_USE, &private->flags)) {
+               set_bit(SNSS_REQUESTED, &private->flags);
+               spin_unlock_irqrestore(&snsslock, irqflags);
+               return;
+       };
+       spin_unlock_irqrestore(&snsslock, irqflags);
+       dasd_eer_clean_SNSS_request(cqr);
+       cqr->device = device;
+       cqr->retries = 255;
+       cqr->expires = 10 * HZ;
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = DASD_ECKD_CCW_SNSS;
+       ccw->count = SNSS_DATA_SIZE;
+       ccw->flags = 0;
+       ccw->cda = (__u32)(addr_t)cqr->data;
+
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+       cqr->callback = dasd_eer_SNSS_cb;
+       cqr->callback_data = (void *)device;
+        dasd_add_request_head(cqr);
+
+       return;
+}
+
+/*
+ * This function is called for all triggers. It calls the appropriate
+ * function that writes the actual trigger records.
+ */
+static int
+dasd_eer_write_trigger(struct dasd_eer_trigger *trigger)
+{
+       int rc;
+       struct dasd_eer_private *private = trigger->device->eer;
+
+       switch (trigger->id) {
+       case DASD_EER_FATALERROR:
+       case DASD_EER_PPRCSUSPEND:
+               rc = dasd_eer_write_standard_trigger(
+                       trigger->id, trigger->device, trigger->cqr);
+               break;
+       case DASD_EER_NOPATH:
+               rc = dasd_eer_write_standard_trigger(
+                       trigger->id, trigger->device, NULL);
+               break;
+       case DASD_EER_STATECHANGE:
+                if (queue_work(dasd_eer_workqueue, &private->worker)) {
+                        rc=0;
+                } else {
+                        /* If the work_struct was already queued, it can't
+                         * be queued again. But this is OK since we don't
+                         * need to have it queued twice.
+                         */
+                        rc = -EBUSY;
+                }
+               break;
+       default: /* unknown trigger, so we write it without any sense data */
+               rc = dasd_eer_write_standard_trigger(
+                       trigger->id, trigger->device, NULL);
+               break;
+       }
+       return rc;
+}
+
+/*
+ * This function is registered with the dasd device driver and gets called
+ * for all dasd eer notifications.
+ */
+static int dasd_eer_notify(struct notifier_block *self,
+                           unsigned long action, void *data)
+{
+       switch (action) {
+       case DASD_EER_DISABLE:
+               dasd_eer_disable_on_device((struct dasd_device *)data);
+               break;
+       case DASD_EER_TRIGGER:
+               dasd_eer_write_trigger((struct dasd_eer_trigger *)data);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+
+/*****************************************************************************/
+/*      the device operations                                                */
+/*****************************************************************************/
+
+/*
+ * On the one side we need a lock to access our internal buffer, on the
+ * other side a copy_to_user can sleep. So we need to copy the data we have
+ * to transfer in a readbuffer, which is protected by the readbuffer_mutex.
+ */
+static char readbuffer[PAGE_SIZE];
+DECLARE_MUTEX(readbuffer_mutex);
+
+
+static int
+dasd_eer_open(struct inode *inp, struct file *filp)
+{
+       struct eerbuffer *eerb;
+       unsigned long flags;
+
+       eerb = kmalloc(sizeof(struct eerbuffer), GFP_KERNEL);
+       eerb->head = 0;
+       eerb->tail = 0;
+       eerb->residual = 0;
+       eerb->buffer_page_count = 1;
+       eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
+        eerb->buffer = kmalloc(eerb->buffer_page_count*sizeof(char*),
+                              GFP_KERNEL);
+        if (!eerb->buffer)
+                return -ENOMEM;
+       if (dasd_eer_allocate_buffer_pages(eerb->buffer,
+                                          eerb->buffer_page_count)) {
+               kfree(eerb->buffer);
+               return -ENOMEM;
+       }
+       filp->private_data = eerb;
+       spin_lock_irqsave(&bufferlock, flags);
+       list_add(&eerb->list, &bufferlist);
+       spin_unlock_irqrestore(&bufferlock, flags);
+
+       return nonseekable_open(inp,filp);
+}
+
+static int
+dasd_eer_close(struct inode *inp, struct file *filp)
+{
+       struct eerbuffer *eerb;
+       unsigned long flags;
+
+       eerb = (struct eerbuffer *)filp->private_data;
+       spin_lock_irqsave(&bufferlock, flags);
+       list_del(&eerb->list);
+       spin_unlock_irqrestore(&bufferlock, flags);
+       dasd_eer_free_buffer_pages(eerb->buffer, eerb->buffer_page_count);
+       kfree(eerb->buffer);
+       kfree(eerb);
+
+       return 0;
+}
+
+static long
+dasd_eer_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       int intval;
+       struct eerbuffer *eerb;
+
+       eerb = (struct eerbuffer *)filp->private_data;
+       switch (cmd) {
+       case DASD_EER_PURGE:
+               dasd_eer_purge_buffer(eerb);
+               return 0;
+       case DASD_EER_SETBUFSIZE:
+               if (get_user(intval, (int __user *)arg))
+                       return -EFAULT;
+               return dasd_eer_resize_buffer(eerb, intval);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+static ssize_t
+dasd_eer_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
+{
+       int tc,rc;
+       int tailcount,effective_count;
+        unsigned long flags;
+       struct eerbuffer *eerb;
+
+       eerb = (struct eerbuffer *)filp->private_data;
+       if(down_interruptible(&readbuffer_mutex))
+               return -ERESTARTSYS;
+
+       spin_lock_irqsave(&bufferlock, flags);
+
+       if (eerb->residual < 0) { /* the remainder of this record */
+                                 /* has been deleted             */
+               eerb->residual = 0;
+               spin_unlock_irqrestore(&bufferlock, flags);
+               up(&readbuffer_mutex);
+               return -EIO;
+       } else if (eerb->residual > 0) {
+               /* OK we still have a second half of a record to deliver */
+               effective_count = min(eerb->residual, (int)count);
+               eerb->residual -= effective_count;
+       } else {
+               tc = 0;
+               while (!tc) {
+                       tc = dasd_eer_read_buffer(eerb,
+                               sizeof(tailcount), (char*)(&tailcount));
+                       if (!tc) {
+                               /* no data available */
+                               spin_unlock_irqrestore(&bufferlock, flags);
+                               up(&readbuffer_mutex);
+                               if (filp->f_flags & O_NONBLOCK)
+                                       return -EAGAIN;
+                               rc = wait_event_interruptible(
+                                       dasd_eer_read_wait_queue,
+                                       eerb->head != eerb->tail);
+                               if (rc) {
+                                       return rc;
+                               }
+                               if(down_interruptible(&readbuffer_mutex))
+                                       return -ERESTARTSYS;
+                               spin_lock_irqsave(&bufferlock, flags);
+                       }
+               }
+               WARN_ON(tc != sizeof(tailcount));
+               effective_count = min(tailcount,(int)count);
+               eerb->residual = tailcount - effective_count;
+       }
+
+       tc = dasd_eer_read_buffer(eerb, effective_count, readbuffer);
+       WARN_ON(tc != effective_count);
+
+       spin_unlock_irqrestore(&bufferlock, flags);
+
+       if (copy_to_user(buf, readbuffer, effective_count)) {
+               up(&readbuffer_mutex);
+               return -EFAULT;
+       }
+
+       up(&readbuffer_mutex);
+       return effective_count;
+}
+
+static unsigned int
+dasd_eer_poll (struct file *filp, poll_table *ptable)
+{
+       unsigned int mask;
+       unsigned long flags;
+       struct eerbuffer *eerb;
+
+       eerb = (struct eerbuffer *)filp->private_data;
+       poll_wait(filp, &dasd_eer_read_wait_queue, ptable);
+       spin_lock_irqsave(&bufferlock, flags);
+       if (eerb->head != eerb->tail)
+               mask = POLLIN | POLLRDNORM ;
+       else
+               mask = 0;
+       spin_unlock_irqrestore(&bufferlock, flags);
+       return mask;
+}
+
+static struct file_operations dasd_eer_fops = {
+       .open           = &dasd_eer_open,
+       .release        = &dasd_eer_close,
+       .unlocked_ioctl = &dasd_eer_ioctl,
+       .compat_ioctl   = &dasd_eer_ioctl,
+       .read           = &dasd_eer_read,
+       .poll           = &dasd_eer_poll,
+       .owner          = THIS_MODULE,
+};
+
+static struct miscdevice dasd_eer_dev = {
+       .minor      = MISC_DYNAMIC_MINOR,
+       .name       = "dasd_eer",
+       .fops       = &dasd_eer_fops,
+};
+
+
+/*****************************************************************************/
+/*     Init and exit                                                        */
+/*****************************************************************************/
+
+static int
+__init dasd_eer_init(void)
+{
+       int rc;
+
+       dasd_eer_workqueue = create_singlethread_workqueue("dasd_eer");
+       if (!dasd_eer_workqueue) {
+               MESSAGE(KERN_ERR , "%s", "dasd_eer_init could not "
+                      "create workqueue \n");
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       rc = dasd_register_eer_notifier(&dasd_eer_nb);
+       if (rc) {
+               MESSAGE(KERN_ERR, "%s", "dasd_eer_init could not "
+                      "register error reporting");
+               goto queue;
+       }
+
+       dasd_ioctl_no_register(THIS_MODULE, BIODASDEERSET, dasd_ioctl_set_eer);
+       dasd_ioctl_no_register(THIS_MODULE, BIODASDEERGET, dasd_ioctl_get_eer);
+
+       /* we don't need our own character device,
+        * so we just register as misc device */
+       rc = misc_register(&dasd_eer_dev);
+       if (rc) {
+               MESSAGE(KERN_ERR, "%s", "dasd_eer_init could not "
+                      "register misc device");
+               goto unregister;
+       }
+
+       return 0;
+
+unregister:
+       dasd_unregister_eer_notifier(&dasd_eer_nb);
+       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDEERSET,
+                                dasd_ioctl_set_eer);
+       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDEERGET,
+                                dasd_ioctl_get_eer);
+queue:
+       destroy_workqueue(dasd_eer_workqueue);
+out:
+       return rc;
+
+}
+module_init(dasd_eer_init);
+
+static void
+__exit dasd_eer_exit(void)
+{
+       dasd_unregister_eer_notifier(&dasd_eer_nb);
+       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDEERSET,
+                                dasd_ioctl_set_eer);
+       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDEERGET,
+                                dasd_ioctl_get_eer);
+       destroy_workqueue(dasd_eer_workqueue);
+
+       WARN_ON(misc_deregister(&dasd_eer_dev) != 0);
+}
+module_exit(dasd_eer_exit);